Spring Security自定义登录与验证

Spring Security 核心组件有:

  1. SecurityContext:security安全上下文,通过校验之后,验证信息存储 SecurityContext中。
  2. SecurityContextHolder:存储认证信息。
  3. Authentication:存储当前用户。
  4. UserDetails:用户信息。
  5. AuthenticationManager:作用就是校验Authentication,如果验证失败会抛出AuthenticationException异常。

框架的认证过程:
用户名密码->Authentication(未认证) -> AuthenticationManager->AuthenticationProvider->UserDetailService->UserDetails->Authentication(已认证)

所以步骤大致有:
第一步:我们定义自己的用户信息类 UserInfo 继承UserDetails和Serializable接口

package com.sjh.security;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;

import java.io.Serializable;
import java.util.Collection;

/**
 * 用户信息bean
 */
public class UserInfo implements UserDetails, Serializable{
    private static final long serialVersionUID = 8155405987900665123L;
    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) {
        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() {
        return AuthorityUtils.commaSeparatedStringToAuthorityList(role);
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", role='" + role + '\'' +
                ", accountNonExpired=" + accountNonExpired +
                ", accountNonLocked=" + accountNonLocked +
                ", credentialsNonExpired=" + credentialsNonExpired +
                ", enabled=" + enabled +
                '}';
    }
}

第二步:写一个UserDetailsService接口的实现类来返回这个UserInfo的对象实例

package com.sjh.security;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * 加载用户信息,根据用户名
 */
@Component
public class MyUserDetailsService implements UserDetailsService{
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //这里可以可以通过s(登录时输入的用户名)然后到数据库中找到对应的用户信息,并构建成我们自己的UserInfo来返回。
        if("admin".equals(s)){
            return new UserInfo("admin", "123456", "ROLE_ADMIN", true,true,true, true);
        }
        return null;
    }
}

第三步:实现我们自己的 AuthenticationProvider,新建类 MyAuthenticationProvider 继承AuthenticationProvider

package com.sjh.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 *验证登录
 */
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    //注入自己的UserDetailsService
    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = authentication.getName();// 这个获取表单输入中返回的用户名;
        System.out.println(userName);
        String password = (String) authentication.getCredentials();// 这个是表单中输入的密码;
        UserInfo userInfo = (UserInfo) myUserDetailsService.loadUserByUsername(userName);
        if(userInfo==null){
            throw new BadCredentialsException("用户名不存在");
        }
        if (!userInfo.getPassword().equals(password)) {
            throw new BadCredentialsException("密码不正确");
        }
        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        // 支持执行
        return true;
    }
}

第四步: 自定义登录成功和失败的处理逻辑( 分别继承SavedRequestAwareAuthenticationSuccessHandler和SimpleUrlAuthenticationFailureHandler2个类,并重写其中的部分方法即可。)

package com.sjh.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登录成功
 */
@Component
public class MySavedRequestAwareAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    private ObjectMapper objectMapper;
    private Logger logger = LoggerFactory.getLogger(getClass());
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        //什么都不做的话,直接调用父类的方法
        super.onAuthenticationSuccess(request, response, authentication);
        //如果是返回json格式,那么我们这么写
//        logger.info("登录成功");
//        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));

}
package com.sjh.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 登录失败
 */
@Component
public class MySimpleUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Autowired
    private ObjectMapper objectMapper;
    private Logger logger = LoggerFactory.getLogger(getClass());

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        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));
    }

}

RBAC(role-Based-access control)权限控制
  一个是用户,一个是角色 ,一个是资源(菜单,按钮),其中用户和角色关联,角色和资源关联

/**
* 根据登录的用户,添加权限
*/
@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("/admin/aaa");
        urls.add("/user/getusersbyid");
        // 注意这里不能用equal来判断,因为有些URL是有参数的,所以要用AntPathMatcher来比较
        for (String url : urls) {
            if (antPathMatcher.match(url, request.getRequestURI())) {
               hasPermission = true;
               break;
            }
        }
    }
    return hasPermission;
 }

配置

package com.sjh.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * 配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationProvider provider;
    @Autowired
    private MySavedRequestAwareAuthenticationSuccessHandler mySavedRequestAwareAuthenticationSuccessHandler;
    @Autowired
    private MySimpleUrlAuthenticationFailureHandler mySimpleUrlAuthenticationFailureHandler;
    @Autowired
    private DataSource dataSource;

    /**
     * 密码的加密
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 添加用户以及用户的权限
     *
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth
//            .inMemoryAuthentication()
//            .withUser("admin") // 管理员,同事具有 ADMIN,USER权限,可以访问所有资源
//            .password(passwordEncoder().encode("123"))
//            .roles("ADMIN", "USER")
//            .and()
//            .withUser("user1")
//            .password(passwordEncoder().encode("123")) // 普通用户,只能访问
//            .roles("USER");
        auth.authenticationProvider(provider);
    }

    /**
     * authorizeRequests() 定义哪些URL需要被保护、哪些不需要被保护。
     * formLogin() 定义当需要用户登录时候,转到的登录页面。
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login/form")
//                .failureUrl("/login-error")
                .successHandler(mySavedRequestAwareAuthenticationSuccessHandler)
                .failureHandler(mySimpleUrlAuthenticationFailureHandler)
                .permitAll()  //表单登录,permitAll()表示这个不需要验证
                .and()
                .authorizeRequests()
                .anyRequest().access("@rbacService.hasPermission(request,authentication)")
                .and()
                .csrf().disable();
//                .antMatchers("/user/**").hasRole("USER")
//                .antMatchers("/admin/**").hasRole("ADMIN")
//                .anyRequest().authenticated()

    }

    /**
     *忽略拦截
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
//        web.ignoring().antMatchers("/admin/aaa");
    }
}

页面
  页面的目录
在这里插入图片描述
userlogin.html如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>登录</title>
</head>
<body>
<div class="container">
    <form method="post" action="/login/form">
        <h2>Please sign in</h2>
        <p>
            <label>Username</label>
            <input type="text" name="username"  placeholder="请输入用户名"/>
        </p>
        <p>
            <label>Password</label>
            <input type="password" name="password"  class="form-control" placeholder="请输入密码" />
        </p>
        <button type="submit">登录</button>
    </form>
</div>
</body>
</html>

控制类

package com.sjh.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;


@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "userlogin";
    }
    
//    @GetMapping("/login/form")
//    @ResponseBody
//    public String loginfrom() {
//        return "sssss";
//    }

//    @RequestMapping("/login-error")
//    public String loginError(){
//        return "login-error";
//    }
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值