权限控制-SpringSecurity学习

 

 

1.添加依赖

 <dependency>

                  <groupId>org.springframework.boot</groupId>

                  <artifactId>spring-boot-starter-security</artifactId>
  </dependency>

2.此时访问原有路径会弹出需要登陆

spring security 会默认使用一个用户名为:user 的用户,密码就是 启动的时候生成的(通过控制台console中查看)

3.书写配置

   3.1标注注解 @Configuration

                      @EnableWebSecurity或@EnableGlobalMethodSecurity(prePostEnabled=true)

  3.2 继承父类WebSecurityConfigurerAdapter并重写configure方法;下面是两个demo

 /**
     * 描述:csrf().disable()为了关闭跨域访问的限制,若不关闭则websocket无法与后台进行连接
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable();
        http.csrf().disable().authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/main")
                .failureUrl("/login?error=true")
                .permitAll()
                .and()
                .logout()
                .logoutSuccessUrl("/login").
                permitAll();
    }
   @Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form").failureUrl("/login-error").permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                  .and()
                  .authorizeRequests().anyRequest().authenticated()                  
                  .and()
                  .csrf().disable();            
      }

 

4.在该类中自定义登陆账号与密码

      4.1方法一,注意参数,和3中不一样

 @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // TODO Auto-generated method stub
            
            auth
            .inMemoryAuthentication()
                  .withUser("admin").password("123456").roles("USER")
                  .and()
                  .withUser("test").password("test123").roles("ADMIN");
      }
@Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // TODO Auto-generated method stub
            
            auth
            .inMemoryAuthentication()
                  .withUser("admin").password("123456").roles("USER")
                  .and()
                  .withUser("test").password("test123").roles("ADMIN");
      }

    4.2

@Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth
                  .inMemoryAuthentication()
                        .withUser("admin").password("123456").roles("USER");
            
      }

5.自定义用户认证

       5.1 User继承类UserDetails,注意当中重写的

getAuthorities()方法,其中返回的即是权限列表
public class UserInfo implements Serializable, UserDetails {
      /**
       *
       */
      private static final long serialVersionUID = 1L;
      private String username;
      private String password;
      private String role;
      private boolean accountNonExpired;
      private boolean accountNonLocked;
      private boolean credentialsNonExpired;
      private boolean enabled;
      public UserInfo(String username, String password, String role, boolean accountNonExpired, boolean accountNonLocked,
                  boolean credentialsNonExpired, boolean enabled) {
            // TODO Auto-generated constructor stub
            this.username = username;
            this.password = password;
            this.role = role;
            this.accountNonExpired = accountNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.credentialsNonExpired = credentialsNonExpired;
            this.enabled = enabled;
      }
      // 这是权限
      @Override
      public Collection<? extends GrantedAuthority> getAuthorities() {
            // TODO Auto-generated method stub
            return AuthorityUtils.commaSeparatedStringToAuthorityList(role);
      }
      @Override
      public String getPassword() {
            // TODO Auto-generated method stub
            return password;
      }
      @Override
      public String getUsername() {
            // TODO Auto-generated method stub
            return username;
      }
      @Override
      public boolean isAccountNonExpired() {
            // TODO Auto-generated method stub
            return accountNonExpired;
      }
      @Override
      public boolean isAccountNonLocked() {
            // TODO Auto-generated method stub
            return accountNonLocked;
      }
      @Override
      public boolean isCredentialsNonExpired() {
            // TODO Auto-generated method stub
            return credentialsNonExpired;
      }
      @Override
      public boolean isEnabled() {
            // TODO Auto-generated method stub
            return enabled;
      }
}

5.2 返回用户实例

public class CustomUserService implements UserDetailsService {

    @Inject
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userDao.findByLogin(s);
        if(user == null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        // 自定义错误的文章说明的地址:http://blog.csdn.net/z69183787/article/details/21190639?locationNum=1&fps=1
        if(user.getState().equalsIgnoreCase("0")){
            throw new LockedException("用户账号被冻结,无法登陆请联系管理员!");
        }
        return user;
    }
}

方法二

ublic class MyAuthenticationProvider implements AuthenticationProvider {
      /**
       * 注入我们自己定义的用户信息获取对象
       */
      @Autowired
      private UserDetailsService userDetailService;
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // TODO Auto-generated method stub
            String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
            String password = (String) authentication.getPrincipal();// 这个是表单中输入的密码;
            // 这里构建来判断用户是否存在和密码是否正确
            UserInfo userInfo = (UserInfo) userDetailService.loadUserByUsername(userName); // 这里调用我们的自己写的获取用户的方法;
            if (userInfo == null) {
                  throw new BadCredentialsException("用户名不存在");
            }
            // //这里我们还要判断密码是否正确,实际应用中,我们的密码一般都会加密,以Md5加密为例
            // Md5PasswordEncoder md5PasswordEncoder=new Md5PasswordEncoder();
            // //这里第个参数,是salt
            // 就是加点盐的意思,这样的好处就是用户的密码如果都是123456,由于盐的不同,密码也是不一样的,就不用怕相同密码泄漏之后,不会批量被破解。
            // String encodePwd=md5PasswordEncoder.encodePassword(password, userName);
            // //这里判断密码正确与否
            // if(!userInfo.getPassword().equals(encodePwd))
            // {
            // throw new BadCredentialsException("密码不正确");
            // }
            // //这里还可以加一些其他信息的判断,比如用户账号已停用等判断,这里为了方便我接下去的判断,我就不用加密了。
            //
            //
            if (!userInfo.getPassword().equals("123456")) {
                  throw new BadCredentialsException("密码不正确");
            }
            Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
            // 构建返回的用户登录成功的token
            return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
      }
      @Override
      public boolean supports(Class<?> authentication) {
            // TODO Auto-generated method stub
            // 这里直接改成retrun true;表示是支持这个执行
            return true;
      }
}

6.再次修改配置

    @Autowired
    private AuthenticationProvider provider;  //注入我们自己的AuthenticationProvider


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO Auto-generated method stub
        auth.authenticationProvider(provider);


//        auth
//        .inMemoryAuthentication()
//            .withUser("admin").password("123456").roles("USER")
//            .and()
//            .withUser("test").password("test123").roles("ADMIN");
    }
@Autowired
    private AuthenticationProvider provider;  //注入我们自己的AuthenticationProvider


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // TODO Auto-generated method stub
        auth.authenticationProvider(provider);


//        auth
//        .inMemoryAuthentication()
//            .withUser("admin").password("123456").roles("USER")
//            .and()
//            .withUser("test").password("test123").roles("ADMIN");
    }
@RequestMapping("/whoim")
      public Object whoIm()
      {
            return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      }
@RequestMapping("/whoim")
      public Object whoIm()
      {
            return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      }

如果要使用或者获取,可以通过@Autowired  SecurityContextHolder来获取;他是全局对象,也可以把获取方法放入baseController中,所有Controller获取即可;

7.自定义成功和失败处理逻辑

在现在的大多数应用中,一般都是前后端分离的,所以我们登录成功或失败都需要用json格式返回,或者登录成功之后,跳转到某个具体的页面。

写两个类,分别继承SavedRequestAwareAuthenticationSuccessHandler 和SimpleUrlAuthenticationFailureHandler2

//处理登录成功的。
@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{
      
      @Autowired
      private ObjectMapper objectMapper;
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                  throws IOException, ServletException {            
            //什么都不做的话,那就直接调用父类的方法
            super.onAuthenticationSuccess(request, response, authentication);  
            
            //这里可以根据实际情况,来确定是跳转到页面或者json格式。
            //如果是返回json格式,那么我们这么写
            
            Map<String,String> map=new HashMap<>();
            map.put("code", "200");
            map.put("msg", "登录成功");
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(map));
            
            
            //如果是要跳转到某个页面的,比如我们的那个whoim的则
            new DefaultRedirectStrategy().sendRedirect(request, response, "/whoim");
            
      }
}

 

//登录失败的
@Component("myAuthenticationFailHander")
public class MyAuthenticationFailHander extends SimpleUrlAuthenticationFailureHandler {
      @Autowired
      private ObjectMapper objectMapper;
      private Logger logger = LoggerFactory.getLogger(getClass());
      @Override
      public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                  AuthenticationException exception) throws IOException, ServletException {
            // TODO Auto-generated method stub
            logger.info("登录失败");
            //以Json格式返回
            Map<String,String> map=new HashMap<>();
            map.put("code", "201");
            map.put("msg", "登录失败");
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");   
            response.getWriter().write(objectMapper.writeValueAsString(map));
            
      }
}

 

      @Autowired
      private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
      @Autowired
      private AuthenticationFailureHandler myAuthenticationFailHander;
      
      @Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                  .and()
                  .authorizeRequests().anyRequest().authenticated()                  
                  .and()
                  .csrf().disable();            
      }

8.对每个Url进行权限判断

/**
 * 返回权限验证的接口
 * 
 *
 */
public interface RbacService {
      boolean hasPermission(HttpServletRequest request,Authentication authentication);
}

@Component("rbacService")
public class RbacServiceImpl implements RbacService {
      private AntPathMatcher antPathMatcher = new AntPathMatcher();
      @Override
      public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
            Object principal = authentication.getPrincipal();
            boolean hasPermission = false;
            if (principal instanceof UserDetails) { //首先判断先当前用户是否是我们UserDetails对象。
                  String userName = ((UserDetails) principal).getUsername();
                  Set<String> urls = new HashSet<>(); // 数据库读取 //读取用户所拥有权限的所有URL
                  
                  urls.add("/whoim");
                  // 注意这里不能用equal来判断,因为有些URL是有参数的,所以要用AntPathMatcher来比较
                  for (String url : urls) {
                        if (antPathMatcher.match(url, request.getRequestURI())) {
                              hasPermission = true;
                              break;
                        }
                  }
            }
            return hasPermission;
      }
}
@Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                  .and()
                  .authorizeRequests()
//                      .antMatchers("/index").permitAll()                    
//                .antMatchers("/whoim").hasRole("ADMIN")
//                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
//                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                  .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必须经过认证以后才能访问            
                  .and()
                  .csrf().disable();            
      }

9.token存储

9.1建表

CREATE TABLE persistent_logins (
    username VARCHAR(64) NOT NULL,
    series VARCHAR(64) NOT NULL,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL,
    PRIMARY KEY (series)
);
(
    username VARCHAR(64) NOT NULL,
    series VARCHAR(64) NOT NULL,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL,
    PRIMARY KEY (series)
);

9.2获取数据

 @Autowired
      private DataSource dataSource;   //是在application.properites

      /**
       * 记住我功能的token存取器配置
       * @return
       */
      @Bean
      public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
      }
@Autowired
      private DataSource dataSource;   //是在application.properites

      /**
       * 记住我功能的token存取器配置
       * @return
       */
      @Bean
      public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
            return tokenRepository;
      }
 @Override
      protected void configure(HttpSecurity http) throws Exception {
            // TODO Auto-generated method stub
            //super.configure(http);
            http
                  .formLogin().loginPage("/login").loginProcessingUrl("/login/form")
                  .successHandler(myAuthenticationSuccessHandler)
                  .failureHandler(myAuthenticationFailHander)
                  .permitAll()  //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
                  .and()
                  .rememberMe()
                        .rememberMeParameter("remember-me").userDetailsService(userDetailsService)
                        .tokenRepository(persistentTokenRepository())
                        .tokenValiditySeconds(60)
                  .and()
                  .authorizeRequests()
//                      .antMatchers("/index").permitAll()                    
//                .antMatchers("/whoim").hasRole("ADMIN")
//                .antMatchers(HttpMethod.POST,"/user/*").hasRole("ADMIN")
//                .antMatchers(HttpMethod.GET,"/user/*").hasRole("USER")
                  .anyRequest().access("@rbacService.hasPermission(request,authentication)")    //必须经过认证以后才能访问            
                  .and()
                  .csrf().disable();      

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值