SSM integrates Spring Security

This document mainly records that the SSM framework integrates Spring Security to perform account login operations. MyBatis has been integrated and basic operations have been omitted

Entity User

First, you need to define the dto of the User entity

public class User{
    @Id//Identifying the primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY) //Self growth strategy
    private Long id;

    private String email;

    private String password;

    private String phone;

    private String nickName;

    private String state;

    private String imgUrl;

    private String enable;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }

   public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

    public String getPassword() {
        return password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone == null ? null : phone.trim();
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName == null ? null : nickName.trim();
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state == null ? null : state.trim();
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl == null ? null : imgUrl.trim();
    }

    public String getEnable() {
        return enable;
    }

    public void setEnable(String enable) {
        this.enable = enable == null ? null : enable.trim();
    }

    }

Interface to implement UserDetails

The framework needs entities of type UserDetails, so we need to implement the User interface of UserDetails, and then override all his methods

Account information needs to contain Role information, so a Role attribute has been added

public class User implements UserDetails {
    @Id//Identifying the primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY) //Self growth strategy
    private Long id;

    private String email;

    private String password;

    private String phone;

    private String nickName;

    private String state;

    private String imgUrl;

    private String enable;

    @Transient
    protected List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(roles == null || roles.size()<=0){
            return null;
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        for(Role r:roles){
            authorities.add(new SimpleGrantedAuthority(r.getRoleValue()));
        }
        return authorities;
    }

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

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

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

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

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

    @Override
    public boolean isEnabled() {
        if(StringUtils.isNotBlank(state) && "1".equals(state) && StringUtils.isNotBlank(enable) && "1".equals(enable)){
            return true;
        }
        return false;
    }

    public void setPassword(String password) {
        this.password = password == null ? null : password.trim();
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone == null ? null : phone.trim();
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName == null ? null : nickName.trim();
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state == null ? null : state.trim();
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl == null ? null : imgUrl.trim();
    }

    public String getEnable() {
        return enable;
    }

    public void setEnable(String enable) {
        this.enable = enable == null ? null : enable.trim();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof User) {
            return getEmail().equals(((User)obj).getEmail())||getUsername().equals(((User)obj).getUsername());
        }
        return false;
    }
    @Override
    public int hashCode() {
        return getUsername().hashCode();
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                ", nickName='" + nickName + '\'' +
                ", state='" + state + '\'' +
                ", imgUrl='" + imgUrl + '\'' +
                ", enable='" + enable + '\'' +
                ", roles=" + roles +
                '}';
    }
}
public SimpleGrantedAuthority(String role) {
    Assert.hasText(role, "A granted authority textual representation is required");
    this.role = role;
}

The construction method of SimpleGrantedAuthority is String type,

The getAuthorities method is a method to obtain user role information for authorization. Different roles can have different permissions.

To distinguish whether it is the same user, override the equals and hashCode methods.

Rewriting toString to output information

Implement the UserDetailsService interface

The way Spring Security provides to get user information doesn't meet our needs, so we make our own way to get user information

We need to get role information as well as user information

Entity class defining Role

public class Role {
    @Id//Identifying the primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY) //Self growth strategy
    private Long id;

    private String roleName;

    private String roleValue;

    private String roleMatcher;

    private String enabled;

    private String remark;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName == null ? null : roleName.trim();
    }

    public String getRoleValue() {
        return roleValue;
    }

    public void setRoleValue(String roleValue) {
        this.roleValue = roleValue == null ? null : roleValue.trim();
    }

    public String getRoleMatcher() {
        return roleMatcher;
    }

    public void setRoleMatcher(String roleMatcher) {
        this.roleMatcher = roleMatcher == null ? null : roleMatcher.trim();
    }

    public String getEnabled() {
        return enabled;
    }

    public void setEnabled(String enabled) {
        this.enabled = enabled == null ? null : enabled.trim();
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark == null ? null : remark.trim();
    }
}

Because not only user information but also role confidence are obtained, role data needs to be queried according to employee Code

Add the corresponding method in RoleService:

/**
     * Query all roles based on user id
     * @param uid
     * @return
     */
    List<Role> findByUid(Long uid);

Add the corresponding method in the corresponding Mapper
 

/**
     * Query role information according to user id
     * @param uid
     * @return
     */
    List<Role> findByUid(@Param("uid")Long uid);
<?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="yuan.boke.www.dao.RoleMapper">
    <select id="findByUid"  resultMap="roleListMap">
      select id,role_name,role_value,enabled from role where id in(select r.r_id from user u, role_user r where u.id = r.u_id and r.u_id = #{uid}) and enabled =1
  </select>

    <resultMap type="yuan.boke.www.entity.Role" id="roleListMap">
        <id property="id" column="id" />
        <result property="roleName" column="role_name" />
        <result property="roleValue" column="role_value" />
        <result property="enabled" column="enabled" />
    </resultMap>
</mapper>

Implement its method RoleServiceImpl in the implementation class

public class RoleServiceImpl implements RoleService {
    @Autowired
    RoleMapper roleMapper;
    @Override
    public List<Role> findByUid(Long uid) {
        return roleMapper.findByUid(uid);
    }
}

Implement UserDetailsService, create the class AccountDetailsService, rewrite the account query to return the entity of UserDetails

public class AccountDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userService.findByEmail(email);
        if(user == null){
            throw new UsernameNotFoundException("Wrong user name or password");
        }
        List<Role> roles = roleService.findByUid(user.getId());
        user.setRoles(roles);
        return user;
    }
}

Inherit UsernamePasswordAuthenticationFilter filter

UsernamePasswordAuthenticationFilter is a filter that processes user authentication logic. Inheriting it, you can make your own authentication logic and add your own authentication logic processing to it

Implement UsernamePasswordAuthenticationFilter filter class by creating AccountAuthenticationFilter

public class AccountAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private String codeParameter = "code";

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = this.obtainUsername(request);
        String password = this.obtainPassword(request);
        String code = this.obtainCode(request);
        String caChecode = (String)request.getSession().getAttribute("VERCODE_KEY");
        boolean flag = CodeValidate.validateCode(code,caChecode);
        if(!flag){
            throw new UsernameNotFoundException("Verification code error");
        }
        if(username == null) {
            username = "";
        }

        if(password == null) {
            password = "";
        }
        username = username.trim();
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        this.setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }


    protected String obtainCode(HttpServletRequest request) {
        return request.getParameter(this.codeParameter);
    }
}

Encapsulate the user name and password into the UsernamePasswordAuthenticationToken object

Implement the AccessDeniedHandler interface

When users access unprotected resources, unified return is provided to ensure their friendliness

Create MyAccessDeniedHandler to implement the AccessDeniedHandler class

public class MyAccessDeniedHandler implements AccessDeniedHandler {

    private String errorPage;

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        boolean isAjax = "XMLHttpRequest".equals(httpServletRequest.getHeader("X-Requested-With"));
        if (isAjax) {
            String jsonObject = "{\"message\":\"Access is denied!\",\"access-denied\":true}";
            String contentType = "application/json";
            httpServletResponse.setContentType(contentType);
            PrintWriter out = httpServletResponse.getWriter();
            out.print(jsonObject);
            out.flush();
            out.close();
            return;
        } else {
            if (!httpServletResponse.isCommitted()) {
                if (this.errorPage != null) {
                    httpServletRequest.setAttribute("SPRING_SECURITY_403_EXCEPTION", e);
                    httpServletResponse.setStatus(403);
                    RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher(this.errorPage);
                    dispatcher.forward(httpServletRequest, httpServletResponse);

                } else {
                    httpServletResponse.sendError(403, e.getMessage());
                }
            }
        }
    }
    public void setErrorPage(String errorPage) {
        if(errorPage != null && !errorPage.startsWith("/")) {
            throw new IllegalArgumentException("errorPage must begin with '/'");
        } else {
            this.errorPage = errorPage;
        }
    }
}

spring-security.xml configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <security:http security="none" pattern="/css/**" />
    <security:http security="none" pattern="/js/**" />
    <security:http security="none" pattern="/images/**" />
    <security:http security="none" pattern="/favicon.ico"/>
    <security:http security="none" pattern="/login*" />
    <security:http security="none" pattern="/checkCode"/>
    <security:http security="none" pattern="/checkEmail"/>
    <security:http security="none" pattern="/checkPhone"/>
    <security:http security="none" pattern="/captchaServlet"/>
    <security:http security="none" pattern="/activecode*"/>
    <security:http security="none" pattern="/sendEmail*"/>
    <security:http security="none" pattern="/register*" />
    <security:http security="none" pattern="/doRegister" />
    <security:http security="none" pattern="/accessDenied"/>
	<security:http security="none" pattern="/reply"/>

    <security:http auto-config="false" access-decision-manager-ref="accessDecisionManager"
                    use-expressions="true" entry-point-ref="loginEntryPoint">
		
		 <security:headers>
            <security:frame-options disabled="true"></security:frame-options>
		 </security:headers>
	 
        <security:form-login login-page="/login" authentication-failure-url="/login?error=1"
                             login-processing-url="/doLogin" password-parameter="password"
                             default-target-url="/list"
                             username-parameter="username" />

        <security:access-denied-handler ref="accessDeniedHandler" />
       <!-- Prohibit csrf-->
        <security:csrf disabled="true"/>
        <security:intercept-url pattern="/" access="permitAll"/>
        <security:intercept-url pattern="/index**" access="permitAll"/>
        <security:intercept-url pattern="/sendSms" access="permitAll"/>
        <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')"/>

        <!-- session Invalid url session strategy-->
        <security:session-management invalid-session-url="/index.jsp"  session-authentication-strategy-ref="sessionStrategy">
        </security:session-management>

        <!-- spring-security Filter provided and our custom filter authenticationFilter-->
        <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER" />
        <security:custom-filter before="FORM_LOGIN_FILTER" ref="authenticationFilter"/>
        <security:custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER"/>
    </security:http>
    <bean id="accessDeniedHandler"
                class="yuan.boke.www.security.account.MyAccessDeniedHandler">
        <property name="errorPage" value="/accessDenied.jsp" />
    </bean>

    <bean id="loginEntryPoint"
          class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <!-- Default landing page url -->
        <constructor-arg value="/login?error=login"/>
    </bean>

    <!-- Enable expression to prepare for later voter -->
    <bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"
          id="expressionHandler"/>
    <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"
          id="expressionVoter">
        <property name="expressionHandler" ref="expressionHandler"/>
    </bean>

    <!-- Authentication manager, using custom accountService,And password md5 encryption -->
    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider user-service-ref="accountService">
            <security:password-encoder hash="md5">
                <security:salt-source user-property="username"></security:salt-source>
            </security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

    <bean id="authenticationFilter" class="yuan.boke.www.security.account.AccountAuthenticationFilter">
        <property name="filterProcessesUrl" value="/doLogin"></property>
        <property name="authenticationManager" ref="authenticationManager"></property>
        <property name="sessionAuthenticationStrategy" ref="sessionStrategy"></property>
        <property name="authenticationSuccessHandler">
            <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
                <property name="defaultTargetUrl" value="/list"></property>
            </bean>
        </property>
        <property name="authenticationFailureHandler">
            <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
                <property name="defaultFailureUrl" value="/login?error=fail"></property>
            </bean>
        </property>
    </bean>

    <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <!-- Process exiting virtual url -->
        <property name="filterProcessesUrl" value="/loginout" />
        <!-- Default display after successful exit processing url -->
        <constructor-arg index="0" value="/login?logout" />
        <constructor-arg index="1">
            <!-- After exiting successfully handler list -->
            <array>
                <bean id="securityContextLogoutHandler"
                      class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
            </array>
        </constructor-arg>
    </bean>

    <!-- ConcurrentSessionFilter Filter configuration(Main setup account session Expiration path) -->
    <bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
        <constructor-arg ref="sessionRegistry"></constructor-arg>
        <constructor-arg value="/login?error=expired"></constructor-arg>
    </bean>

    <bean id="sessionStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
        <constructor-arg>
            <list>

                <bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
                    <property name="maximumSessions" value="1"></property>
                    <property name="exceptionIfMaximumExceeded" value="false"></property>
                    <constructor-arg ref="sessionRegistry"/>
                </bean>
                <bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/>
                <bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
                    <constructor-arg ref="sessionRegistry"/>
                </bean>
            </list>

        </constructor-arg>
    </bean>
    <bean id="sessionRegistry" scope="singleton" class="org.springframework.security.core.session.SessionRegistryImpl"></bean>
    <bean id="accountService" class="yuan.boke.www.security.account.AccountDetailsService"/>

    <!-- An access decision voter that reads ROLE_* configuration settings -->
    <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
    <bean id="authenticatedVoter"
          class="org.springframework.security.access.vote.AuthenticatedVoter"/>

    <bean id="accessDecisionManager"
          class="org.springframework.security.access.vote.AffirmativeBased">
        <constructor-arg>
            <list>
                <ref local="roleVoter"/>
                <ref local="authenticatedVoter"/>
                <ref local="expressionVoter"/>
            </list>
        </constructor-arg>
    </bean>

</beans>

web.xml configuration

Configuring Spring Security's permission filter chain in web.xml

 <!-- Spring Security Permission filter chain for-->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Introducing spring-security.xml into web.xml

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath*:spring-mybatis.xml,
            classpath*:applicationContext-redis.xml,
            classpath*:applicationContext-activemq.xml,
            classpath:applicationContext-solr.xml,
            classpath:spring-security.xml
        </param-value>

    </context-param>

 

Published 14 original articles, won praise and 10000 visitors+
Private letter follow

Tags: Session xml Spring Mybatis

Posted on Thu, 16 Jan 2020 05:20:51 -0800 by Gondwana