實現思路
使用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
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持。