springboot后端如何实现携带token登陆
作者:nian002
实现思路
使用oauth2+redis+mysql来完成登陆校验,本案例使用oauth2简单的密码模式来实现。
最终实现的效果为
- 登陆页面不设置权限审核,用户通过登陆界面登陆,输入账户密码,后端接收到账户密码之后会去数据库验证,如果验证通过,则返回token给前端。
- 除了登陆页面之外,其余的页面访问的时候会进行权限的鉴定,如果携带的token对应用户的权限不足或没有携带token、携带了错误的token,不允许访问。
- token具有时限,超时token会失效,可以通过refresh_token来刷新token的持续时间。
项目结构
项目的结构为
├─.idea │ └─dictionaries ├─log ├─src │ ├─main │ │ ├─java │ │ │ └─Rush │ │ │ ├─config │ │ │ ├─controller │ │ │ ├─mapper │ │ │ ├─pojo │ │ │ ├─service │ │ │ └─util │ │ └─resource │ └─test │ └─target
项目的重点其实也就在于config包内
config包内定义了4个类:
AuthorizationServerConfig
ResourceServerConfig
WebSecurityConfig
CORSFilter
「额外针对OAuth跨域问题」
除此之外,继承了UserDetails接口的User类,继承UserDetailsService类的UserService类也很关键。
除了这6个类之外,别的类和普通mybatis项目无异
AuthorizationServerConfig
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import org.springframework.web.bind.annotation.CrossOrigin; @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired AuthenticationManager authenticationManager; @Autowired RedisConnectionFactory redisConnectionFactory; @Autowired UserDetailsService userDetailsService; @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("password") .authorizedGrantTypes("password", "refresh_token") .accessTokenValiditySeconds(1800) .resourceIds("rid") .scopes("all") .secret("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); } }
ResourceServerConfig
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.web.bind.annotation.CrossOrigin; @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("rid").stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .antMatchers("/user/**").hasAnyRole("user","admin") .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/home").permitAll() .and() .csrf().disable(); } }
WebSecurityConfig
import bocRush.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.bind.annotation.CrossOrigin; @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserService userService; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean @Override protected UserDetailsService userDetailsService() { return super.userDetailsService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // auth.inMemoryAuthentication() // .withUser("admin") // .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") // .roles("admin") // .and() // .withUser("sang") // .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") // .roles("user"); auth.userDetailsService(userService); } @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/oauth/**").authorizeRequests() .antMatchers("/oauth/**").permitAll() .and().cors() .and().csrf().disable(); } }
CORSFilter
/** * Date : 2021/3/25 17:48 * Author : nicolas */ import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /*全局跨域配置*/ @Order(Ordered.HIGHEST_PRECEDENCE) @Configuration public class CORSFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,PATCH,HEAD"); response.setHeader("Access-Control-Allow-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "*"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { filterChain.doFilter(servletRequest, servletResponse); } } @Override public void destroy() { } }
User
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class User implements UserDetails { private Integer id; private String username; private String password; private Boolean enabled; private Boolean locked; private List<Role> roles; @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())); } return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return !locked; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return enabled; } //省略getter/setter public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public Boolean getLocked() { return locked; } public void setLocked(Boolean locked) { this.locked = locked; } public List<Role> getRoles() { return roles; } public void setRoles(List<Role> roles) { this.roles = roles; } }
UserService
import bocRush.mapper.UserInfoMapper; import bocRush.mapper.UserMapper; import bocRush.pojo.User; import bocRush.pojo.UserInfo; import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class UserService implements UserDetailsService { @Autowired UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userMapper.loadUserByUsername(username); if (user == null) { throw new UsernameNotFoundException("账户不存在!"); } user.setRoles(userMapper.getUserRolesByUid(user.getId())); //System.out.println(user.getRoles().get(1).getName() + " --- " + user.getUsername()); return user; } }
对于接口的传参数
获取token
- 使用POST方法
http://localhost:8080/oauth/token?username=sang&password=123&grant_type=password&client_id=password&scope=all&client_secret=123
刷新token
- 使用POST方法
http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=1a1c67a0-5f9b-49b1-9f95-dc7889c85cf5&client_id=password&client_secret=123
携带token访问资源
在url内直接携带token
- GET方法
http://localhost:8080/user/hello?access_token=9bdde947-19b7-46fe-8fe0-0f2804150768
在header内携带token
- GET方法
http://localhost:8080/admin/hello
------------Header----------------
Authorization : bearer 3929d92d-f5be-4b2d-9223-8b13e2412f14
Accept : application/json
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。