SpirngBoot整合SpringSecurity简单示例

  • 相关pom依赖

     <dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-security</artifactId>
     </dependency>
     <dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-aop</artifactId>
     </dependency>
     <dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     		<dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-thymeleaf</artifactId>
     </dependency>
    
  • 如果需要security去帮你做用户认证的话,需让用户实体类去实现UserDetails接口,这里面的type属性代表权限

 public class User implements UserDetails {

    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;

    @Override    //设置账号不过期
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override //账号未锁定
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override //凭证未过期
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override //账号可用
    public boolean isEnabled() {
        return true;
    }
    
    @Override //得到权限
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (getType()) {
                    case 1:
                        return "ADMIN";
                    default:
                        return "USER"; //否则
                }
            }
        });
        return list;
    }

}
  • UserService类,这里需要实现UserDetailsService接口,如果不需要security认证的话,可以不去实现。
 @Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }

	//security需要我们在loadUserByUsername提供一个可以查用户的方法,这里直接返回findUserByName就可以了
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
         return this.findUserByName(s);
    }
}
  • 接下来就可以去写我们的配置类了,SecurityConfig
 @Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**"); //忽略静态资源的访问
    }

    //AuthenticationManager 认证的核心接口
    //AuthenticationManagerBuilder 用于构建AuthenticationManager对象的工具
    //providerManager: AuthenticationManager接口的默认实现类
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //内置的认证规则,这里的盐值是固定的,而实际如果我们有自己的加密规则的话,一般不去使用这个方法
        //auth.userDetailsService(userService).passwordEncoder(new Pbkdf2PasswordEncoder("12345"));//加盐加密

        //自定义认证规则
        //AuthenticationProvider: providerManager持有一组AuthenticationProvider,每个AuthenticationProvider负责一种认证(登录时不只账号密码一种方式,第三方登录也是一种方式,兼容所有的登录方式)
        //providerManager将认证委托给AuthenticationProvider
        auth.authenticationProvider(new AuthenticationProvider() {  //账号密码认证方式
            //Authentication 用于封装认证信息(账号密码),不同方式封装不同的认证信息
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                String username = authentication.getName();
                String password = (String) authentication.getCredentials();
                User user = userService.findUserByName(username);
                if(user == null) {
                    throw new UsernameNotFoundException("账号不存在"); //security会有filter去接收异常
                }

                password = WeUtil.md5(password + user.getSalt());//对明文密码进行Md5加盐加密
                if (!user.getPassword().equals(password)){
                    throw new BadCredentialsException("密码不正确");
                }
                return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()); // 认证主要信息, 证书, 权限
            }

            //返回当前的认证类型
            @Override
            public boolean supports(Class<?> aClass) {
                //UsernamePasswordAuthenticationToken: Authentication接口的常用实现类
                return UsernamePasswordAuthenticationToken.class.equals(aClass);
            }
        });
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);  //覆盖父类的方法,查看源码可以看出security为我们提供了默认的认证界面,如果不覆盖,所有请求都会通过默认的认证,同学们可以去测试一下,引入依赖后security就会立刻接管我们的认证,这里就不展示了
        //登录相关的配置
        http.formLogin().loginPage("/loginpage") //登录界面
                .loginProcessingUrl("/login") //认证的url映射
                //若认证成功
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index"); //重定向,会向客户端传递新的请求,request被重置
                    }
                })
                //若认证失败
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                        request.setAttribute("error",e.getMessage());
                        request.getRequestDispatcher("/loginpage").forward(request, response);   //转发,客户端可以通过request得到信息
                    }
                });
        http.logout().logoutUrl("/logout")  //退出
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                        response.sendRedirect(request.getContextPath() + "/index");
                    }
                });

        //授权配置
        http.authorizeRequests().antMatchers("/letter").hasAnyAuthority("USER","ADMIN") //拥有USER和ADMIN权限的可以请求/letter
                .antMatchers("/admin").hasAnyAuthority("ADMIN")  
                .and().exceptionHandling().accessDeniedPage("/denied"); //权限不够时,返回的请求

        //增加filter处理验证码,addFilterBefore,如果验证码错误,则不进行用户名密码的认证
        http.addFilterBefore(new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                HttpServletResponse response = (HttpServletResponse) servletResponse;
                if(request.getContextPath().equals("/login")) {
                    String code = request.getParameter("vertifyCode");
                    if(code == null || !code.equalsIgnoreCase("1234") ) { //这里把验证码写死了,只是做一个测试
                        request.setAttribute("error","验证码错误");
                        request.getRequestDispatcher("/loginpage").forward(request, response);
                        return;
                    }
                }
                filterChain.doFilter(request, response); //让请求向下执行,去找下一个filter,否则请求到此终止
            }
        }, UsernamePasswordAuthenticationFilter.class);  //UsernamePasswordAuthenticationFilter,在用户名密码验证之前执行

        //记住我
        http.rememberMe()
                .tokenRepository(new InMemoryTokenRepositoryImpl())  //把用户存到内存里,要想存到redis里,需要自己实现接口
                .tokenValiditySeconds(3600*24) //3600s*24
                .userDetailsService(userService);
    }
}
  • 加密工具类
 public class WeUtil {

    public static String md5(String key) {
        if (StringUtils.isBlank(key)) {
            return null;
        }
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }

}
  • IndexController
@Controller
public class IndexController {

    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        //认证成功后,认证结果会通过SecurityContextHolder存入SecurityContext中
        Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (obj instanceof User) {
            model.addAttribute("user",obj);
        }
        return "/index";
    }

    @RequestMapping(path = "/discuss", method = RequestMethod.GET)  //不需要权限即可访问
    public String getDiscussPage() {
        return "/site/discuss";
    }

    @RequestMapping(path = "/letter", method = RequestMethod.GET) //有USER权限的可以访问
    public String getLetterPage() {
        return "/site/letter";
    }

    @RequestMapping(path = "/admin", method = RequestMethod.GET) //有ADMIN权限的可以访问
    public String getAdminPage() {
        return "/site/admin";
    }

    @RequestMapping(path = "/loginpage", method = {RequestMethod.GET, RequestMethod.POST})
    public String getLoginPage() {
        return "/site/login";
    }


    // 
    @RequestMapping(path = "/denied", method = {RequestMethod.GET})
    public String getDeniedPage() {
        return "/error/404";
    }

}
  • UserMapper
    @Mapper
public interface UserMapper {

    User selectByName(String username);

}
  • mapper.xml
 <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.we.community.dao.UserMapper">

    <sql id="selectFields">
        id, username, password, salt, email, type, status, activation_code, header_url, create_time
    </sql>

    <select id="selectByName" resultType="User">
        select
        <include refid="selectFields"></include>
        from user
        where username = #{username}
    </select>

</mapper>
  • 首页
 <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

    <h1>社区首页</h1>

    <p th:if="${user!=null}">
        欢迎你, <span th:text="${user.username}"></span>!
    </p>

    <ul>
        <li><a th:href="@{/discuss}">帖子详情</a></li>
        <li><a th:href="@{/letter}">私信列表</a></li>
        <li><a th:href="@{/loginpage}">登录</a></li>
<!-- security要求退出需用post -->
        <li>
            <form method="post" th:action="@{/logout}">
                <a href="javascript:document.forms[0].submit();">退出</a>
            </form>
        </li>

    </ul>

</body>
</html>
  • 登录页面。 admin, discuss, letter界面只是一句话,这里就不贴了
 <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>

    <h1>登录社区</h1>
    
    <form method="post" th:action="@{/login}">
        <p style="color:red;" th:text="${error}">
            <!--提示信息-->
        </p>
        <p>
            账号:<input type="text" name="username" th:value="${username}">
        </p>
        <p>
            密码:<input type="password" name="password" th:value="${password}">
        </p>
        <p>
            验证码:<input type="text" name="vertifyCode">
        </p>
        <p>
            <input type="submit" value="登录"> 
        </p>
        <p>
            <input type="checkbox" name="remember-me"> 记住我
        </p>
    </form>

</body>
</html>

示例中只用type字段代表了权限,如果需要更复杂的授权时,需要自己设计权限表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值