- 1.创建项目,引入依赖
- 2.security的其他配置方式
- 2.2基于内存
- 2.3HttpSecurity(入门配置此文件)
- 2.4多个HttpSecurity的配置(复杂业务场景下)—-security
- 2.5配置方法安全
- 2.6基于数据库的认证(基础入门)——-sercuty-db/sercurity-dy
- 2.7spring security整合oauth2—-oauth2
- 2.8spring security支持json登录—-security-dy/security-db/oauth2
- 2.9spring security整合jwt —jwt-demo
- 2.10 oauth2 整合jwt+swagger —-swcurity-swagger/ 松哥例子swagger-jwt
- -1.configure其他配置
decurity-demo.sql 数据库验证的文件
密码是123或者123456/ 可以使用 new BCryptPasswordEncoder().encode(“123”);加密出来
模板项目地址https://gitee.com/find_me/java-findme
1.创建项目,引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
引入依赖后,项目中所有的接口就会被保护起来,项目默认用户名未user,密码为启动后随即生成的,在控制台打印的密码
2.security的其他配置方式
spring:
security:
user:
name: find me
password: 123456
roles: admin
2.2基于内存
当然,开发者也可以自定义类继承自WebSecurityConfigurerAdapter ,进而实现对 Spring Security
更多的自定义配置,例如基于内存的认证,配置方式如下:
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
// 密码不加密的配置 ,过时的方法
@Bean
//PasswordEncoder passwordEncoder () {
//return NoOpPasswordEncoder. getlnstance () ;
// }
// 密码需要加密
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//基于内存的配置
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("admin")
.and()
.withUser("江南一点雨").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("user");
}
代码解释:
·自定义 MyWebSecurityConfig 继承自 WebSecurityConfigurerAdapter ,并重写configure(AuthenticationManagerBuilder auth)方法,在该方法中配直两个用户,一个用户名是
admin,密码123 ,具备两个角色 ADMIN USER 另一个用户名是 findme,密码是 123 ,具备一个角色USER
Spring Security 本是 0.6 Spring Security 中引入了 多种密码加密方式,开发者必须指定其中一种, NoOpPasswordEncoder ,即不对密码进行加密。
注意:基于配置和内存的方式在配置角色的时候不需要添加”ROLE_”.基于数据库的需要
2.3HttpSecurity(入门配置此文件)
根据实际情况进行角色配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private ObjectMapper objectMapper;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 配置登录的账号
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("admin")
.and()
.withUser("江南一点雨").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("user");
}
//基本的httpSecurity配置
// 1.请求路径角色
// 2.配置表单登录
// 3.配置登录成功,失败或者登出的处理方式
// authentication里面存放登录成功后的信息
// {
// "password": null, 密码
// "username": "javaboy",
// "authorities": [ 具备的角色
// {
// "authority": "ROLE_admin"
// }
// ],
// "accountNonExpired": true, 账户没有过期,
// "accountNonLocked": true, 账户没有锁定
// "credentialsNonExpired": true, 密码没有过期
// "enabled": true 账户可用
// },
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()//开启配置
.antMatchers("/admin/**").hasRole("admin")//路径符合这个需要admin角色
// .antMatchers("user/**").hasAnyRole("admin", "user")//路径符合这个,需要这两个的任意一个
.antMatchers("/user/**").access("hasAnyRole('user','admin')")
.anyRequest().authenticated()//其他请求,登录后就可以访问
.and()
.formLogin()//表单登录
.loginProcessingUrl("/doLogin")//处理登录请求的地址
.loginPage("/login")//配置登录页面(实际上还是一个请求地址) 前后端分类不存在这种页面 这里还是访问的应该请求,会根据这请求去返回登录页面
.usernameParameter("uname")//修改登录的key
.passwordParameter("passwd")//修改登录的key
.successHandler(new AuthenticationSuccessHandler() { //登录成功的处理
@Override// authentication保存了登录成功后的信息
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", authentication.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() {//登录失败的处理
@Override // e登录失败的异常
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账户被锁定,登录失败!");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "用户名或密码输入错误,登录失败!");
} else if (e instanceof DisabledException) {
map.put("msg", "账户被禁用,登录失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账户过期,登录失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码过期,登录失败!");
} else {
map.put("msg", "登录失败!");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
.and()
.logout()
.logoutUrl("/logout") //配置注销请求的地址
.logoutSuccessHandler(new LogoutSuccessHandler() {//注销成功后的回调
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", "注销登录成功!");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
.csrf().disable();
/**
* 权限不足处理
*/
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
Map<String, Object> failureMap = new HashMap<>();
failureMap.put("code", 403);
failureMap.put("msg", "权限不足!");
httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));
}
});
// /**
// * 未登陆处理
// */
// http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
// @Override
// public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
//
//
// httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
//
// Map<String, Object> failureMap = new HashMap<>();
// failureMap.put("code", 401);
// failureMap.put("msg", "请先登录!");
//
// httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));
//
// }
// });
}
}
2.4多个HttpSecurity的配置(复杂业务场景下)—-security
参考项目 spring-security-me/security
采用静态内部类的方式 采用order设置优先级,数字越小,优先级越大
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true) //开启方法安全
public class MultiHttpSecurityConfig {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy").password("$2a$10$G3kVAJHvmRrr6sOj.j4xpO2Dsxl5EG8rHycPHFWyi9UMIhtdSH15u").roles("admin")
.and()
.withUser("江南一点雨").password("$2a$10$kWjG2GxWhm/2tN2ZBpi7bexXjUneIKFxIAaMYJzY7WcziZLCD4PZS").roles("user");
}
@Configuration
@Order(1)
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasAnyRole("admin");
//只有这种格式的路径才会去要求角色
}
}
@Configuration
public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.csrf().disable();
}
}
}
2.5配置方法安全
在Security上面加上
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true) //开启方法安全 然后在方法上加PreAuthorize注解
@Service
public class MethodService {
@PreAuthorize("hasRole('admin')")// 这个方法需要admin角色
public String admin() {
return "hello admin";
}
@Secured("ROLE_user") //需要user角色
public String user() {
return "hello user";
}
@PreAuthorize("hasAnyRole('admin','user')")//需要两者之一
public String hello() {
return "hello hello";
}
}
2.6基于数据库的认证(基础入门)——-sercuty-db/sercurity-dy
1.定义实体类user(继承UserDetails)和role
/**
* @Author 江南一点雨
* UserDetails相当于一个规范
* @Site www.javaboy.org 2019-08-11 17:08
*/
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// 返回用户所有的角色
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
// 重新整理一下 角色认证要以ROLE_开始 数据库没有就要在这里手动拼接
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String getUsername() {
return username;
}
// 账户是否未过期 目前数据库没有就直说true
@Override
public boolean isAccountNonExpired() {
return true;
}
// 账户是否未锁定
@Override
public boolean isAccountNonLocked() {
return !locked;
}
// 密码是否未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 是否可用
@Override
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
}
2.配置SecurityConfig
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:16
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 此处去调用数据库的或者采用静态数据
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//对于不同的路径要不同的角色 静态配置
http.authorizeRequests()
.antMatchers("/dba/**").hasRole("dba")
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
// 角色继承的bean
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
// 以前
String hierarchy = "dba > admin admin > user";
// 现在
String hierarchy = "dba > admin \n admin > user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
3.动态权限配置的Security写法
1. 1.先定义MyFilter实现FilterInvocationSecurityMetadataSource
主要就是分析出需要哪些角色
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:16
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
// @Bean
// RoleHierarchy roleHierarchy() {
// RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
// String hierarchy = "dba > admin \n admin > user";
// roleHierarchy.setHierarchy(hierarchy);
// return roleHierarchy;
// }
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
2.编写MyAccessDecisionManager继承 AccessDecisionManager
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:38
*/
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
*
* @param authentication 当前用户具备的角色
* @param o
* @param collection 当前路径需要的角色,这里是在MyFiter处理后得到的
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute attribute : collection) {
if ("ROLE_login".equals(attribute.getAttribute())) {
// 如果是返回的ROLE_login说明你请求的路径不存在,所有判断你有没有登录 登录的就直接放行
if (authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException("非法请求!");
} else {
return;
}
}
// 获取我具备的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 做匹配
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(attribute.getAttribute())) {
return;
}
}
}
// 例如,我具备某些角色,但是此角色没有此路径的权限,那就是非要请求
throw new AccessDeniedException("非法请求!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
3.1SecurityConfig的写法
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:16
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {//FilterSecurityInterceptor 拦截器
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
2.7spring security整合oauth2—-oauth2
2.7.1配置资源服务器 ResourceServerConfig
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-12 23:15
* 资源服务器
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//指定资源id stateless配置基于令牌认证
resources.resourceId("rid").stateless(true);
}
//此处角色路径可以从数据库加载
// @Override
// public void configure(HttpSecurity http) throws Exception {
// // 路径的角色
// http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
// .antMatchers("/user/**").hasRole("user")
// .anyRequest().authenticated();
// }
// 从数据库加载如下
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
/**
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
2.7.2授权服务器 AuthorizationServerConfig
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-12 23:01
* 授权服务器
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
// 支持password的认证模式
@Autowired
AuthenticationManager authenticationManager;
// 配置redis就会有
@Autowired
RedisConnectionFactory redisConnectionFactory;
// 密码加密方式
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置在内存中
//配置认证模式
//配置授权模式
//资源id(名字)
//配置的密码123456
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(1800)
.resourceIds("rid")
.scopes("all")
.secret("$2a$10$LcM2.fVWzB50vitKLrPPDugS/owlp.qVVT5jA0EyJuFeez6S5hTkm");
}
// 配置令牌的存储
@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();
}
}
2.7.3security配置文件
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
// 静态的加载用户
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .withUser("javaboy").password("$2a$10$LcM2.fVWzB50vitKLrPPDugS/owlp.qVVT5jA0EyJuFeez6S5hTkm").roles("admin")
// .and()
// .withUser("江南一点雨")
// .password("$2a$10$kwLIAqAupvY87OM.O25.Yu1QKEXV1imAv7jWbDaQRFUFWSnSiDEwG")
// .roles("user");
// }
// 从数据库加载用户
@Autowired
UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
//放开oauth相关的请求,不就行拦截
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().csrf().disable();
}
2.7.4 MyFilter
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:24
*/
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
// 做路径匹配的 提供了路径批评规则
AntPathMatcher pathMatcher = new AntPathMatcher();
@Autowired
MenuService menuService;
/**
* 分析请求地址 根据请求的地址,分析出需要哪些角色
* @param o
* @return
* @throws IllegalArgumentException
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
// 请求的地址
String requestUrl = ((FilterInvocation) o).getRequestUrl();
// 所有的菜单 ,这里可以给一个缓存
List<Menu> allMenus = menuService.getAllMenus();
for (Menu menu : allMenus) {
// 如果请求地址批评上了
if (pathMatcher.match(menu.getPattern(), requestUrl)) {
List<Role> roles = menu.getRoles();
String[] rolesStr = new String[roles.size()];
//查看需要哪些角色 rolesStr,防止角色集合
for (int i = 0; i < roles.size(); i++) {
rolesStr[i] = roles.get(i).getName();
}
return SecurityConfig.createList(rolesStr);
}
}
// 没有匹配上路径,这个就是标识符号,看在数据库中没有这个路径,怎么处理
return SecurityConfig.createList("ROLE_login");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
2.7.5 MyAccessDecisionManager
/**
* @Author 江南一点雨
* @Site www.javaboy.org 2019-08-11 17:38
*/
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
*
* @param authentication 当前用户具备的角色
* @param o
* @param collection 当前路径需要的角色,这里是在MyFiter处理后得到的
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute attribute : collection) {
if ("ROLE_login".equals(attribute.getAttribute())) {
// 如果是返回的ROLE_login说明你请求的路径不存在,所有判断你有没有登录 登录的就直接放行
if (authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException("非法请求!");
} else {
return;
}
}
// 获取我具备的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 做匹配
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(attribute.getAttribute())) {
return;
}
}
}
// 例如,我具备某些角色,但是此角色没有此路径的权限,那就是非要请求
throw new AccessDeniedException("非法请求!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
2.7.6 postman测试 -请求token
post http://127.0.0.1:8981/oauth/token Body x-www-form-urlencoded [{“key”:”username”,”value”:”admin”}, {“key”:”password”,”value”:”123”}, {“key”:”grant_type”,”value”:”password”}, {“key”:”client_id”,”value”:”password”}, {“key”:”scope”,”value”:”all”}, {“key”:”client_secret”,”value”:”123456”}]
{
"username":"admin",
"password":"123",
"grant_type":"password",
"client_id":"password",
"scope":"all",
"client_secret":"123456",
}
结果 { “access_token”: “d1bdfd01-e06a-4b4e-a661-a49012da9afa”, “token_type”: “bearer”, “refresh_token”: “d31082b6-68a7-46b5-937a-62a24b186970”, “expires_in”: 1325, “scope”: “all” }
换回新的token
{
"grant_type":"refresh_token",
"refresh_token":"d31082b6-68a7-46b5-937a-62a24b186970",
"grant_type":"password",
"client_secret":"123456",
}
2.8spring security支持json登录—-security-dy/security-db/oauth2
2.8.1重写UsernamePasswordAuthenticationFilter方法
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws
AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
//说明用户以 JSON 的形式传递的参数
String username = null;
String password = null;
try {
Map<String, String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = map.get("username");
password = map.get("password");
} catch (IOException e) {
e.printStackTrace();
}
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request, response);
}
2.8.2注入重写的方法
在securityconfig中写入一下文件
// 配置支持json登录
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
/**
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
2.8.3配置登录成功后的转跳和失败后转跳
在myAuthenticationFilter中配置
此时成功和失败的回调只能在fiter中配置,cnfigure中的配置是失效的
// 配置支持json登录
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() { //登录成功的处理
@Override// authentication保存了登录成功后的信息
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", authentication.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
});
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {//登录失败的处理
@Override // e登录失败的异常
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账户被锁定,登录失败!");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "用户名或密码输入错误,登录失败!");
} else if (e instanceof DisabledException) {
map.put("msg", "账户被禁用,登录失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账户过期,登录失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码过期,登录失败!");
} else {
map.put("msg", "登录失败!");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
});
return filter;
}
2.9spring security整合jwt —jwt-demo
2.9.1 JwtLoginFilter 登录的时候给用户token
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {
// 实现构造方法
/**
*
* @param defaultFilterProcessesUrl
* @param authenticationManager
*/
public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
setAuthenticationManager(authenticationManager);
}
/**
* 提取用户名和密码去做登录
*
* @param req
* @param httpServletResponse
* @return
* @throws AuthenticationException
* @throws IOException
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse httpServletResponse)
throws AuthenticationException, IOException {
// 为了获取登录的数据转换成user
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
}
// 登录成功的回调 生成jwt
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse resp,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();//获取登录用户的角色
StringBuffer sb = new StringBuffer();
for (GrantedAuthority authority : authorities) {
//获取当前角色
sb.append(authority.getAuthority()).append(",");
}
// 生成jwt的token
String jwt = Jwts.builder()
.claim("authorities", sb)// 用户的角色
.setSubject(authResult.getName()) // 主题
.setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // 过期时间
.signWith(SignatureAlgorithm.HS512, "findme") // 签名的算法
.compact();
Map<String, String> map = new HashMap<>();
map.put("token", jwt);
map.put("msg", "登录成功");
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
// 登录失败
/**
*
*
* @param req
* @param resp
* @param failed
* @throws IOException
* @throws ServletException 登录失败的异常,根据异常判断失败的原因
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException failed) throws IOException, ServletException {
Map<String, String> map = new HashMap<>();
map.put("msg", "登录失败");
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
2.9.2. 访问系统的时候效验token
public class JwtFilter extends GenericFilterBean {
/**
* 用户每次登录的时候校验token
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
// token 放在很多地方都可以,此处是默认把token放到请求头中
String jwtToken = req.getHeader("authorization");
// 解析token
Jws<Claims> jws = Jwts.parser().setSigningKey("findme") // 生成token的签名
.parseClaimsJws(jwtToken.replace("Bearer", "")); // 前端的token会自动加一个Bearer,此处需要去掉
Claims claims = jws.getBody(); // 登录的信息
String username = claims.getSubject();// 用户名 刚才在生成的时候放入了
// 拿到登录的角色后要转换成一个list集合解析 此处是当前用户的角色
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
// new 一个新的token
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(token);
// 对过滤器放行
filterChain.doFilter(servletRequest,servletResponse);
}
2.9.3增加security的相关配置 configure(_HttpSecurity http) _
// jwt 相关配置
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login")
.permitAll()
.anyRequest().authenticated()
.and()
// 登录的过滤器
.addFilterBefore(new JwtLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
// 做校验的过滤器
.addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable();
2.10 oauth2 整合jwt+swagger —-swcurity-swagger/ 松哥例子swagger-jwt
2.10.1.AccessTokenConfig
package com.find.securityswagger.auth;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
/**
* @ClassName AccessTokenConfig
* @Description token
* @Author find me
* @Date 2020/6/25 0025 20:37
* @Version 1.0
*/
@Configuration
public class AccessTokenConfig {
// TokenStore 我们使用 JwtTokenStore 这个实例。
// 使用了 JWT,access_token 实际上就不用存储了(无状态登录,服务端不需要保存信息),
// 因为用户的所有信息都在 jwt 里边,所以这里配置的 JwtTokenStore 本质上并不是做存储。
@Bean
TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
// 另外我们还提供了一个 JwtAccessTokenConverter,
// 这个 JwtAccessTokenConverter 可以实现将用户信息和 JWT 进行转换(将用户信息转为 jwt 字符串,或者从 jwt 字符串提取出用户信息)。
// 另外,在 JWT 字符串生成的时候,我们需要一个签名,这个签名需要自己保存好。
@Bean
JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("findme");
return converter;
}
}
2.10.2AuthorizationServer
package com.find.securityswagger.auth;
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.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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.Arrays;
/**
* @ClassName JwtAccessTokenConverter
* @Description 将用户信息和 JWT 进行转换
* @Author find me
* @Date 2020/6/25 0025 20:39
* @Version 1.0
*/
@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
TokenStore tokenStore;
@Autowired
ClientDetailsService clientDetailsService;
@Autowired
AuthenticationManager authenticationManager;
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
JwtAccessTokenConverter jwtAccessTokenConverter;
//主要用来配置 Token 的一些基本信息,
// 例如 Token 是否支持刷新、Token 的存储位置、Token 的有效期以及刷新 Token 的有效期等等。
// Token 有效期这个好理解,刷新 Token 的有效期我说一下,
// 当 Token 快要过期的时候,我们需要获取一个新的 Token,在获取新的 Token 时候,
// 需要有一个凭证信息,这个凭证信息不是旧的 Token,而是另外一个 refresh_token,这个 refresh_token 也是有有效期的。
@Bean
AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
services.setClientDetailsService(clientDetailsService);
services.setSupportRefreshToken(true);
services.setTokenStore(tokenStore);
services.setAccessTokenValiditySeconds(60 * 60 * 24 * 2);
services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
services.setTokenEnhancer(tokenEnhancerChain);
return services;
}
//用来配置令牌端点的安全约束,也就是这个端点谁能访问,谁不能访问。
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
//用来配置客户端的详细信息
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("findme")
.secret(passwordEncoder.encode("123")) //
.resourceIds("rid")//资源id
.authorizedGrantTypes("password", "refresh_token")//授权类型
.scopes("all")
.redirectUris("http://localhost:6004/index.html");//重定向 uri
}
// 这里用来配置令牌的访问端点和令牌服务。
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenServices(tokenServices());
}
}
2.10.3ResourceServerConfig
package com.find.securityswagger.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
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.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
/**
* @ClassName ResourceServerConfig
* @Description
* @Author find me
* @Date 2020/6/25 0025 20:41
* @Version 1.0
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
TokenStore tokenStore;
//首先在 configure 方法中配置资源 ID 和 TokenStore,这里配置好之后,
// 会自动调用 JwtAccessTokenConverter 将 jwt 解析出来,jwt 里边就
// 包含了用户的基本信息,所以就不用远程校验 access_token 了。
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//指定资源id stateless配置基于令牌认证
// resources.resourceId("rid").stateless(true);
resources.resourceId("rid").tokenStore(tokenStore);
}
@Autowired
MyFilter myFilter;
@Autowired
MyAccessDecisionManager myAccessDecisionManager;
/**
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
2.10.4 GlobalCorsConfiguration —-支持跨域
package com.find.securityswagger.swaggerconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @ClassName GlobalCorsConfiguration
* @Description 支持跨域
* @Author find me
* @Date 2020/6/25 0025 21:36
* @Version 1.0
*/
@Configuration
public class GlobalCorsConfiguration {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
package com.find.securityswagger.swaggerconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Arrays;
import java.util.List;
/**
* @ClassName Swagger2Config
* @Description swagger
* @Author find me
* @Date 2020/6/25 0025 21:19
* @Version 1.0
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
// // 手动添加token的方式
// @Bean
// Docket docket() {
// return new Docket(DocumentationType.SWAGGER_2)
// .select()
// .apis(RequestHandlerSelectors.basePackage("com.find.securityswagger.controller"))
// .paths(PathSelectors.any())
// .build()
// .securityContexts(Arrays.asList(securityContexts()))
// .securitySchemes(Arrays.asList(securitySchemes()))
// .apiInfo(new ApiInfoBuilder()
// .description("接口文档的描述信息")
// .title("微人事项目接口文档")
// .contact(new Contact("javaboy","https://www.yuque.com/findme","2354827879@qq.com"))
// .version("v1.0")
// .license("Apache2.0")
// .build());
// }
// //通过 securitySchemes 来配置全局参数,这里的配置是一个名为 Authorization 的请求头(OAuth2 中需要携带的请求头)。
// private SecurityScheme securitySchemes() {
// return new ApiKey("Authorization", "Authorization", "header");
// }
//
// // 则用来配置有哪些请求需要携带 Token,这里我们配置了所有请求。
// private SecurityContext securityContexts() {
// return SecurityContext.builder()
// .securityReferences(defaultAuth())
// .forPaths(PathSelectors.any())
// .build();
// }
//
// // 参数
// private List<SecurityReference> defaultAuth() {
// AuthorizationScope authorizationScope = new AuthorizationScope("xxx", "描述信息");
// AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
// authorizationScopes[0] = authorizationScope;
// return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
// }
// 可以在页面去配置整个登录信息
@Bean
Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.find.securityswagger.controller"))
.paths(PathSelectors.any())
.build()
.securityContexts(Arrays.asList(securityContext()))
.securitySchemes(Arrays.asList(securityScheme()))
.apiInfo(new ApiInfoBuilder()
.description("接口文档的描述信息")
.title("find me demo ")
.contact(new Contact("findme","https://www.yuque.com/findme","2354827879@qq.com"))
.version("v1.0")
.license("Apache2.0")
.build());
}
private AuthorizationScope[] scopes() {
return new AuthorizationScope[]{
new AuthorizationScope("all", "all scope")
};
}
// 构建时即得配置 token 的获取地址。
private SecurityScheme securityScheme() {
GrantType grant = new ResourceOwnerPasswordCredentialsGrant("http://127.0.0.1:6004/oauth/token");
return new OAuthBuilder().name("OAuth2")
.grantTypes(Arrays.asList(grant))
.scopes(Arrays.asList(scopes()))
.build();
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(Arrays.asList(new SecurityReference("OAuth2", scopes())))
.forPaths(PathSelectors.any())
.build();
}
}
swagger的两种配置的效果如下
输入 Bearer ${token}
postman测试参数如下 先获取token,在传递token
-1.configure其他配置
// 配置不做拦截的地址
@Override
public void configure(WebSecurity web) throws Exception {
// 返回login页就直接过,不用拦截
web.ignoring().antMatchers("/login");
web.ignoring().antMatchers("/swagger-ui.html")
.antMatchers("/webjars/**")
.antMatchers("/v2/**")
.antMatchers("/swagger-resources/**");
// web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()//开启配置
.antMatchers("/admin/**").hasRole("admin")//路径符合这个需要admin角色
// .antMatchers("user/**").hasAnyRole("admin", "user")//路径符合这个,需要这两个的任意一个
.antMatchers("/user/**").access("hasAnyRole('user','admin')")
.anyRequest().authenticated()//其他请求,登录后就可以访问
.and()
.formLogin()//表单登录
.loginProcessingUrl("/doLogin")//处理登录请求的地址
.loginPage("/login")//配置登录页面(实际上还是一个请求地址) 前后端分类不存在这种页面 这里还是访问的应该请求,会根据这请求去返回登录页面
.usernameParameter("uname")//修改登录的key
.passwordParameter("passwd")//修改登录的key
.successHandler(new AuthenticationSuccessHandler() { //登录成功的处理
@Override// authentication保存了登录成功后的信息
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", authentication.getPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() {//登录失败的处理
@Override // e登录失败的异常
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 401);
if (e instanceof LockedException) {
map.put("msg", "账户被锁定,登录失败!");
} else if (e instanceof BadCredentialsException) {
map.put("msg", "用户名或密码输入错误,登录失败!");
} else if (e instanceof DisabledException) {
map.put("msg", "账户被禁用,登录失败!");
} else if (e instanceof AccountExpiredException) {
map.put("msg", "账户过期,登录失败!");
} else if (e instanceof CredentialsExpiredException) {
map.put("msg", "密码过期,登录失败!");
} else {
map.put("msg", "登录失败!");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
.and()
.logout()
.logoutUrl("/logout") //配置注销请求的地址
.logoutSuccessHandler(new LogoutSuccessHandler() {//注销成功后的回调
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("status", 200);
map.put("msg", "注销登录成功!");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.and()
.csrf().disable();
/**
* 权限不足处理
*/
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
Map<String, Object> failureMap = new HashMap<>();
failureMap.put("code", 403);
failureMap.put("msg", "权限不足!");
httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));
}
});
// /**
// * 未登陆处理
// */
// http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
// @Override
// public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
//
//
// httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
//
// Map<String, Object> failureMap = new HashMap<>();
// failureMap.put("code", 401);
// failureMap.put("msg", "请先登录!");
//
// httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));
//
// }
// });
//