1.spring security简单的demo-适合萌新

1.spring security入门

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("findme").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("findme").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

权限模型
权限模型.png
权限实际开发模型
权限实际开发模型.png

1.定义实体类user(继承UserDetails)和role

security-demo.sql


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


@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

主要就是分析出需要哪些角色



@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


@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的写法

@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
/**
 * 资源服务器
 */
@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

/**
 * 授权服务器
 */
@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("finde")
//                .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


@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

@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

注意在引入依赖的时候引入cloud相关的
E~7BT8XQEO722`7U6MEZIX3.png

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的两种配置的效果如下
img
输入 Bearer ${token}
img
postman测试参数如下 先获取token,在传递token
image.png
image.png

2.11configure其他配置


    @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));
//
//            }
//        });
//

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\],问题中提到的错误是"Cannot resolve org.springframework.security:spring-security-config:5.3.2.RELEASE"。这个错误通常表示在项目的pom文件中找不到所需的Spring Security依赖。根据引用\[1\]中的pom文件片段,可以看到项目中已经添加了spring-boot-starter-security依赖,但是可能缺少了spring-security-config依赖。 解决这个问题的方法是在项目的pom文件中添加spring-security-config依赖。可以在<dependencies>标签中添加以下代码: ```xml <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>5.3.2.RELEASE</version> </dependency> ``` 这样就可以解决"Cannot resolve org.springframework.security:spring-security-config:5.3.2.RELEASE"的错误了。 #### 引用[.reference_title] - *1* *3* [微服务A读配置中心报Could not locate PropertySource错误](https://blog.csdn.net/weixin_53802962/article/details/113811491)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [springcloud框架搭建遇见的问题及解决办法](https://blog.csdn.net/m0_37546844/article/details/109525353)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值