终于看完慕课网的一个实战视频http://coding.imooc.com/class/134.html
下面来做一个简单的使用springsecurity +JWT的rbac实现
首先创建pom项目
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platformgroupId>
<artifactId>platform-bomartifactId>
<version>Brussels-SR4version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Dalston.SR2version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.2version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
创建子项目
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-oauth2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.socialgroupId>
<artifactId>spring-social-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.socialgroupId>
<artifactId>spring-social-coreartifactId>
dependency>
<dependency>
<groupId>org.springframework.socialgroupId>
<artifactId>spring-social-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.socialgroupId>
<artifactId>spring-social-webartifactId>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
dependency>
<dependency>
<groupId>commons-collectionsgroupId>
<artifactId>commons-collectionsartifactId>
dependency>
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.7.0version>
dependency>
dependencies>
创建一个springboot启动类(略)
创建一个Bean注册的类
@Configuration
public class SecurityBeanConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
创建一个搜索比较用户名密码的类
@Component
public class MyUserDetailsService implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(MyUserDetailsService.class);
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, passwordEncoder.encode("123456"),
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
创建一个安全框架的配置类
//@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/authentication/form",
"/authentication/require")
.permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable()
;
}
}
创建一个oauth2的配置类
@Configuration
@EnableResourceServer
@EnableAuthorizationServer//这个注解实现了认证服务器
public class SecurityTokenConfig implements ResourceServerConfigurer,AuthorizationServerConfigurer {
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("liyq")
.secret("liyqSecret")
.accessTokenValiditySeconds(7200)//单位是秒
.authorizedGrantTypes("refresh_token","password")//指定授权模式
.scopes("all","read","write")//这里指定了,发请求可以不带,或者在集合内
;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List enhancers = new ArrayList();
enhancers.add(jwtAccessTokenConverter);
enhancerChain.setTokenEnhancers(enhancers);
endpoints
.tokenEnhancer(enhancerChain)
.accessTokenConverter(jwtAccessTokenConverter);
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/authentication/require")//配置跳转
.loginProcessingUrl("/authentication/form")//from 表单的url
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)//密码登录配置
.and()
.authorizeRequests()
.antMatchers("/authentication/require",
"/authentication/form")
.permitAll()
.and()
.csrf().disable() //关闭csrf防护
;
}
}
这里最好将ResourceServerConfigurer和WebSecurityConfigurerAdapter的实现类和并一下,
只保留一个public void configure(HttpSecurity http) 方法进行配置。
自定义URL
@RestController
public class AuthenticationController {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationController.class);
//把当前请求缓存到session中
private RequestCache RequestCache = new HttpSessionRequestCache();
//这个做跳转
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@RequestMapping("/authentication/require")
public SimpleResponse requireAuthentication(HttpServletRequest request,HttpServletResponse response) throws IOException {
//从session中拿到引发跳转的请求
SavedRequest savedRequest = RequestCache.getRequest(request, response);
if(savedRequest!=null) {
String target = savedRequest.getRedirectUrl();
logger.info("引发跳转的请求是:"+target);
if(StringUtils.endsWithIgnoreCase(target, ".html")) {
logger.debug("url:"+target);
redirectStrategy.sendRedirect(request, response, target);
}
}
return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
}
}
再创建一个JWT的配置类
@Configuration
public class JwtTokenConfig {
@Bean
public TokenStore JwtTokeStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("liyq");
return accessTokenConverter;
}
}
还有一个成功处理器和一个失败处理器
@Component
public class MyFailureHandler implements AuthenticationFailureHandler{
private static final Logger logger = LoggerFactory.getLogger(MyFailureHandler.class);
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
logger.info("失败");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));
}
}
@Component
public class MySucessHandler implements AuthenticationSuccessHandler{
private static final Logger logger = LoggerFactory.getLogger(MySucessHandler.class);
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
logger.info("成功");
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication));
}
}
最后是安全框架跨域请求的问题
一个Filter 配置到WebMvcConfigurer中,
和添加到http.addFilterBefore(myFilter, ChannelProcessingFilter.class)
忽略options请求
builder.ignoring().antMatchers(HttpMethod.OPTIONS);
再然后就是写一个rbac的权限判断方法
@Component("rbacService")
public class RbacServiceImpl implements RbacService{
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
boolean hasPermission = false;
Object principe = authentication.getPrincipal();
if(principe instanceof UserDetails) {
String username = ((UserDetails)principe).getUsername();
//拿到用户名后可以拿到用户角色和用户所有的权限
//读取用户所有的url
Set urls = new HashSet<>();
for(String url : urls) {
if(antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
}
return hasPermission;
}
}
添加配置
config.anyRequest().access("@rbacService.hasPermission(request,authentication)");