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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM