2、spring security中,安全配置通过继承
WebSecurityConfigurerAdapter来配置@Configuration public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception { //做大量的配置 //认证配置 //授权配置 // 例如: http .authorizeRequests() //访问"/"和"/home"路径的请求都允许 .antMatchers("/", "/home","/staff","/staff/*") .permitAll() //而其他的请求都需要认证 .anyRequest() .authenticated() .and() //修改Spring Security默认的登陆界面 .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); }3、添加认证校验 (authentication)
创建数据库,创建一个用户库,用户名和密码
创建访问数据库的接口
public interface UserMapper { User selectByUserName(String userName); List<String> selectAllRoleByUserId(Integer userId); List<String> selectPermissionsByUserId(Integer userId); }创建访问数据库的userMapper.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.demo.mapper.UserMapper"> <select id="selectByUserName" resultType="com.demo.entity.User"> select id,userName,password from t_user where username = #{userName} </select> <select id="selectAllRoleByUserId" resultType="String"> select r.name as name from t_role_user u,t_role r where r.id = u.rid and u.uid=#{userId}; </select> <select id="selectPermissionsByUserId" resultType="String"> SELECT permission FROM t_role r,t_role_user u,t_role_menu rm,t_menu m where r.id = u.uid and rm.mid = m.id and u.rid =rm.rid and u.uid=#{userId} </select> </mapper>新建一个类,实现spring security提供的接口org.springframework.security.core.userdetails.UserDetailsService
这样就会调用该类的方法去访问数据库认证了。这一步中会返回一个User对象,该对象是SpringSecurity里定义的User对象
需要将用户的权限和角色用逗号分割后组装在一起。
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { com.demo.entity.User user = userMapper.selectByUserName(s); if (user == null) { throw new UsernameNotFoundException("用户不存在"); } List<String> roles = userMapper.selectAllRoleByUserId(user.getId()); List<String> permissions = userMapper.selectPermissionsByUserId(user.getId()); StringBuilder sb = new StringBuilder(); for (String role : roles) { sb.append("ROLE_" + role + ","); } for (String permission : permissions) { sb.append(permission + ","); } String rolepermission = sb.substring(0, sb.length() - 1); UserDetails userDetails = new User(user.getUserName(), user.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(rolepermission)); return userDetails; } }注意,
认证的步骤,是先获取用户信息,再校验用户密码的认证过程。
在上步骤中需要使用到一个passwordEncoder编码器,来生成加密密码明文生成密文。
通过java类来配置一个passwordEncoder
@Configuration public class SecurityConfig { @Bean protected PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }4、添加配置,用于完成认证流程管理
登录的界面:/showLogin
登录逻辑入口 /login
登录成功地址 /showMain
登录失败地址 /showFail
.usernameParameter("myusername")以及.passwordParameter("mypassword")定义了登录页面中的表单名称是myusername和mypasswordUsernamePasswordAuthenticationFilter :对/login 的 POST 请求做拦截,校验表单中用户名,密码。
通过源码不难发现:一旦我们使用SpringSecurity来作为认证框架,我们要写自己的登录的接口的话,一定要用post请求,并且用户名和密码是固定的,只能用username和password来作为参数名,因为这是SpringSecurity默认的(如果执意要改,可以通过配置文件进行改)。
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception { http.formLogin() //未登录时的地址 .loginPage("/showLogin") //处理登录请求的url .loginProcessingUrl("/login") //.successForwardUrl("/showMain") //认证成功后跳转的地址 .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.sendRedirect("/showMain"); } }) //.failureForwardUrl("/showFail") //登录失败后的处理器 .usernameParameter("myusername") //客户端的密码参数名称 .passwordParameter("mypassword") .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.sendRedirect("/showFail"); } }); http.exceptionHandling() //.accessDeniedHandler(accessDeniedHandler); //只适用于非前端框架,适用于同步请求的方式 //如果是异步请求需要使用上一种方式。 .accessDeniedPage("/showAccessDenied"); } }5、异常配置
配置异常页面,如果程序异常测跳转到该页面。
如果是异步请求,如ajax,则可以实现
AccessDeniedHandler接口@Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setContentType("application/json;charset=UTF-8"); httpServletResponse.setStatus(httpServletResponse.SC_FORBIDDEN); PrintWriter writer = httpServletResponse.getWriter(); writer.println("权限不足"); writer.flush(); writer.close(); } }6、配置 Remember Me
需要配置生成一个token,并将token下发给浏览器。
因此需要一个PersistentTokenRepository类。
通过java配置类的方式来定义bean,这个bean就是persistentTokenRepository@Configuration public class RememberMeConfig { @Autowired private DataSource dataSource; @Bean protected PersistentTokenRepository getPersistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); //jdbcTokenRepository.setCreateTableOnStartup(true); jdbcTokenRepository.setDataSource(dataSource); return jdbcTokenRepository; } } // 在MyWebSecurityConfigurerAdapter中配置.rememberMe() private MyAccessDeniedHandler accessDeniedHandler; private PersistentTokenRepository persistentTokenRepository; protected void configure(HttpSecurity http) throws Exception { //其他配置 http.rememberMe() .userDetailsService(userDetailsService) .tokenRepository(persistentTokenRepository) .tokenValiditySeconds(10*2); //其他配置 }7、配置授权
授权一般需要依托用户的角色,对权限集合进行绑定。
因为必须先登录,因此对于登录页面直接方行,antMatchers(“/showLogin”,“/showFail”).access(“permitAll”)。
因为静态资源也要直接放行,所以对于.antMatchers(“/images/**”).permitAll() .regexMatchers(“/js/.*”).permitAll() .antMatchers(“/demo”).permitAll()这几个直接放行。
.antMatchers(“/abc”).denyAll() 所有到/abc的请求都拒绝。
最后一行的.anyRequest().authenticated(); 声明所有的请求都需要登录。
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter{ protected void configure(HttpSecurity http) throws Exception { //前面的认证配置 http.authorizeRequests() //.antMatchers("/showLogin","/showFail").permitAll() .antMatchers("/showLogin","/showFail").access("permitAll") //对于静态和动态请求需要分开 //.antMatchers("/js/**").permitAll() .antMatchers("/abc").denyAll() .antMatchers("/jczl").hasAnyAuthority("demo:update") //.antMatchers("/jczl").hasAnyRole("ADMIN") .antMatchers("/admin").access("@myServiceImpl.hasPermission(request,authentication)") .antMatchers("/ip").hasIpAddress("192.168.7.86") .antMatchers("/images/**").permitAll() .regexMatchers("/js/.*").permitAll() .antMatchers("/demo").permitAll() .anyRequest().authenticated();8、配置安全 csrf
为防止跨越的表单提交,在login.html 的隐藏域中需要加上
<input type=“hidden” th:value=“${_csrf.token}” name=“_csrf” th:if=“${_csrf}”>
然后在MyWebSecurityConfigurerAdapter 中继续取消 http.csrf().disable();的注释,即默认开启csrf的校验了。
WebSecurityConfigurerAdapter类:可以通过重载该类的三个configure()方法来制定Web安全的细节。
1、configure(WebSecurity):通过重载该方法,可配置Spring Security的Filter链。
2、configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求。
3、configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail(用户详细信息)服务。
1、使用基于内存的用户存储:
2、基于数据库表进行认证:
\*\* 3、基于LDAP进行认证 \*\*
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 自定义用户认证逻辑 */ @Autowired private UserDetailsService userDetailsService; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * 身份认证接口 从写了这个接口,这里最重要的就是WebSecurityConfigurerAdapter接口下的configure方法,这个方法就是我们要实现的认证逻辑。其实也可以不重写configure方法, 他默认就会去容器里面找PasswordEncoder实现类来作为认证的时候 密码加密 和数据库比较,以及userDetailsService实现类。 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 告诉SpringSecurity 我们要使用自己定义的userDetailsService来通过username来获取主体,并且使用了BCryptPasswordEncoder加密进行密码比较 auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
Spring Security是什么? - 简单例子(三)
本文详细介绍Spring Security的安全配置步骤,包括安全配置的实现、认证校验、Remember Me功能配置、授权配置等,并提供具体代码示例。


被折叠的 条评论
为什么被折叠?



