zoukankan      html  css  js  c++  java
  • Spring Security:整合数据库

    spring security配置文件

    spring security的用户信息从数据库中查询:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:security="http://www.springframework.org/schema/security"
           xmlns:secu="http://www.springframework.org/schema/security"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    			    http://www.springframework.org/schema/beans/spring-beans.xsd
    			    http://www.springframework.org/schema/context
    			    http://www.springframework.org/schema/context/spring-context.xsd
    			    http://www.springframework.org/schema/aop
    			    http://www.springframework.org/schema/aop/spring-aop.xsd
    			    http://www.springframework.org/schema/tx
    			    http://www.springframework.org/schema/tx/spring-tx.xsd
    			    http://www.springframework.org/schema/security
    			    http://www.springframework.org/schema/security/spring-security.xsd">
    
        <!--静态资源不需要认证-->
        <security:http pattern="/css/**" security="none"/>
        <security:http pattern="/img/**" security="none"/>
        <security:http pattern="/plugins/**" security="none"/>
    
        <!--
        auto-config:表示是否自动加载springSecurity的配置文件
        use-expressions 表示是否使用spring的el表达式来配置springSecurity
        -->
        <security:http auto-config="true" use-expressions="true">
            <!--认证页面可以匿名访问-->
            <security:intercept-url pattern="/login.jsp" access="permitAll()"/>
            <!--拦截资源-->
            <!--
            access="hasAnyRole('ROLE_USER') 表示只有ROLE_USER角色才能访问资源
            -->
            <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
            <security:form-login login-page="/login.jsp"
                                 login-processing-url="/login"
                                 default-target-url="/index.jsp"
                                 authentication-failure-url="/failer.jsp"/>
            <!--配置退出登录信息-->
            <security:logout logout-url="/logout" logout-success-url="/login.jsp"/>
            <!--去掉csrf拦截-->
            <security:csrf disabled="false"/>
        </security:http>
    
        <!--设置springSecurity的认证用户信息的来源-->
        <security:authentication-manager>
            <security:authentication-provider user-service-ref="userServiceImpl">
            </security:authentication-provider>
        </security:authentication-manager>
    </beans>
    

    认证

    先写一个service去继承UserDetailsService接口,在去实现方法:

    UserService接口:

    public interface UserService extends UserDetailsService {
    }
    

    实现:

    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Autowired
        private RoleService roleService;
    
        /**
         * @param username 用户在浏览器中输入的用户名
         * @return UserDetails 是SpringSecurity自己的用户对象
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            try{
                SysUser sysUser = userDao.findByName(username);
                if(Objects.isNull(sysUser))
                    return null;
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                for(SysRole role : sysUser.getRoles()){
                    authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
                }
                //{noop}后面的密码,SpringSecurity会认为是密码原文
                return new User(username,"{noop}"+sysUser.getPassword(),authorities);
            }catch (Exception e){
                return null;
            }
        }
    }
    

    实现密码加密

    <!--把加密对象放入ioc容器中-->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>   
        
    <security:authentication-manager>
            <security:authentication-provider user-service-ref="userServiceImpl">
                <security:password-encoder ref="passwordEncoder"/>
            </security:authentication-provider>
        </security:authentication-manager>
    

    认证的时候,把“{noop}”去掉

        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            try{
                SysUser sysUser = userDao.findByName(username);
                if(Objects.isNull(sysUser))
                    return null;
                List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                for(SysRole role : sysUser.getRoles()){
                    authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
                }
                //{noop}后面的密码,SpringSecurity会认为是原文
                return new User(username,sysUser.getPassword(),authorities);
            }catch (Exception e){
                return null;
            }
        }
    

    用户状态

    User对象还有另一个构造方法,含有四个布尔值,用于存取用户的状态。

    只有四个布尔值都为true时,才能登陆成功,否则失败。

    	/**
    	 * Construct the <code>User</code> with the details required by
    	 * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}.
    	 *
    	 * @param username the username presented to the
    	 * <code>DaoAuthenticationProvider</code>
    	 * @param password the password that should be presented to the
    	 * <code>DaoAuthenticationProvider</code>
    	 * @param enabled set to <code>true</code> if the user is enabled
    	 * @param accountNonExpired set to <code>true</code> if the account has not expired
    	 * @param credentialsNonExpired set to <code>true</code> if the credentials have not
    	 * expired
    	 * @param accountNonLocked set to <code>true</code> if the account is not locked
    	 * @param authorities the authorities that should be granted to the caller if they
    	 * presented the correct username and password and the user is enabled. Not null.
    	 *
    	 * @throws IllegalArgumentException if a <code>null</code> value was passed either as
    	 * a parameter or as an element in the <code>GrantedAuthority</code> collection
    	 */
    	public User(String username, String password, boolean enabled,
    			boolean accountNonExpired, boolean credentialsNonExpired,
    			boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
    
    		if (((username == null) || "".equals(username)) || (password == null)) {
    			throw new IllegalArgumentException(
    					"Cannot pass null or empty values to constructor");
    		}
    
    		this.username = username;
    		this.password = password;
    		this.enabled = enabled;
    		this.accountNonExpired = accountNonExpired;
    		this.credentialsNonExpired = credentialsNonExpired;
    		this.accountNonLocked = accountNonLocked;
    		this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
    	}
    

    remember me

    一般网站都提供了记住我的功能,如图所示:

    image-20200930163849859

    SpringSecurity也有对应的功能,在AbstractRememberMeServices类中判断的。

    	protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
    		if (alwaysRemember) {
    			return true;
    		}
    
    		String paramValue = request.getParameter(parameter);
    
    		if (paramValue != null) {
    			if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")
    					|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
    				return true;
    			}
    		}
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Did not send remember-me cookie (principal did not set parameter '"
    					+ parameter + "')");
    		}
    
    		return false;
    	}
    

    配置:手动开启remember-me的过滤器

        <security:http auto-config="true" use-expressions="true">
            <security:remember-me token-validity-seconds="60"/>
        </security:http>
    

    登陆后,我们可以在cookie中看到remember-me的相关信息

    image-20200930165815939

    然后60s后失效。此外手动点击注销,也可以让cookie失效。

    持久化remember me信息

    因为remember me的信息存储在浏览器端,不安全,所以spring security提供了一种更安全的机制:在客户端仅仅保存无意义的加密串(与用户名、密码等敏感数据无关),然后在数据库中保存该字符串与用户的对应关系,自动登录时,用cookie中的加密串,到db中验证,如果通过,自动登陆才算通过。

    创建一张表,注意表的名称和字段名称都不能修改。

    CREATE TABLE `persistent_logins`  (
      `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
      `series` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      `token` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
      `last_used` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
      PRIMARY KEY (`series`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
    

    xml配置:

    <security:remember-me data-source-ref="dataSource" token-validity-seconds="60"/>
    

    配置完成后,重启应用,重新登陆,发现remember-me信息已经被保存到数据库中:

    image-20200930171211167

  • 相关阅读:
    matlab cell
    matlab linux 快捷键设置——有问题还是要解决
    latex 小结
    TOJ 1258 Very Simple Counting
    TOJ 2888 Pearls
    HDU 1248 寒冰王座
    TOJ 3486 Divisibility
    TOJ 3635 过山车
    TOJ 1840 Jack Straws
    HDU 4460 Friend Chains
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/13755542.html
Copyright © 2011-2022 走看看