Documents related:

Spring. IO/projects/sp… www.thymeleaf.org/doc/article…

1.SpringBoot + Security

1.add Depending

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code

2.Config Code

package com.ml.jkeep.internal.auth;

import com.alibaba.fastjson.JSONObject;
import com.ml.jkeep.common.constant.Common;
import com.ml.jkeep.common.constant.ResultMsg;
import com.ml.jkeep.common.vo.RestVo;
import com.ml.jkeep.service.system.impl.AuthServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/** * Security authentication configuration **@authorTan Liangzhong *@date 2019/6/20 10:27
 */
@Slf4j
@EnableWebSecurity
public class JKeepSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthServiceImpl userAuthService;
    @Autowired
    private JKeepSecurityMetadataSource securityMetadataSource;
    @Autowired
    private JKeepAccessDecisionManager accessDecisionManager;
    @Autowired
    private JKeepAccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userAuthService)    // Identity authentication
                .passwordEncoder(new BCryptPasswordEncoder()); // Encryption mode
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(securityMetadataSource); // The filter invokes the security metadata source
                        o.setAccessDecisionManager(accessDecisionManager);    // Authentication policy
                        return o;
                    }
                })
                .and()
                .formLogin()
                .loginPage(Common.LOGIN_PAGE_URL)
                .loginProcessingUrl("/login")
                .usernameParameter("username").passwordParameter("password")
                .failureHandler((req, resp, e) -> {
                    log.error("Login failure handling: {}", JSONObject.toJSONString(req.getParameterMap()));
                    ResultMsg resultMsg;
                    if (e instanceof BadCredentialsException ||
                            e instanceof UsernameNotFoundException) {
                        // The user name or password is incorrect
                        resultMsg = ResultMsg.LOGIN_FAIL_WRONG_PASSWORD;
                    } else if (e instanceof LockedException) {
                        // Account locked, please contact administrator!
                        resultMsg = ResultMsg.LOGIN_FAIL_LOCKED;
                    } else if (e instanceof CredentialsExpiredException) {
                        // Login expired, please log in again!
                        resultMsg = ResultMsg.LOGIN_FAIL_CREDENTIALS_EXPIRED;
                    } else if (e instanceof AccountExpiredException) {
                        // Account expired, please contact administrator!
                        resultMsg = ResultMsg.LOGIN_FAIL_ACCOUNT_EXPIRED;
                    } else if (e instanceof DisabledException) {
                        // Account is disabled, please contact administrator!
                        resultMsg = ResultMsg.LOGIN_FAIL_DISABLED;
                    } else {
                        // Login failed, please contact the administrator!
                        resultMsg = ResultMsg.LOGIN_FAIL;
                    }
                    if(req.getHeader(Common.CSRF_TOKEN_KEY) ! =null) {
                        / / Ajax to submit
                        resp.setStatus(HttpServletResponse.SC_OK);
                        resp.setContentType("application/json; charset=UTF-8");
                        PrintWriter out = resp.getWriter();
                        out.write(JSONObject.toJSONString(RestVo.FAIL(resultMsg)));
                        out.flush();
                        out.close();
                    } else {
                        // Form submission
                        resp.sendRedirect("/login.html");
                    }
                })
                .successHandler((req, resp, auth) -> {
                    if(req.getHeader(Common.CSRF_TOKEN_KEY) ! =null) {
                        / / Ajax to submit
                        resp.setStatus(HttpServletResponse.SC_OK);
                        resp.setContentType("application/json; charset=UTF-8");
                        PrintWriter out = resp.getWriter();
                        out.write(JSONObject.toJSONString(RestVo.SUCCESS("Login successful")));
                        out.flush();
                        out.close();
                    } else {
                        // Form submission
                        resp.sendRedirect("/index");
                    }
                })
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((req, resp, authentication) -> {
                    // Logout successfully redirects the login page
                    if(req.getHeader(Common.CSRF_TOKEN_KEY) ! =null) {
                        / / Ajax to submit
                        resp.setStatus(HttpServletResponse.SC_OK);
                        resp.setContentType("application/json; charset=UTF-8");
                        PrintWriter out = resp.getWriter();
                        out.write(JSONObject.toJSONString(RestVo.SUCCESS("Logout successful")));
                        out.flush();
                        out.close();
                    } else {
                        // Form submission
                        resp.sendRedirect("/login.html");
                    }
                })
                .permitAll()
                .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler) // Access denied policy
                //.and().csrf().disable() // CSRF is enabled by default
            ;
    }

    @Override
    public void configure(WebSecurity web) {
        // Ignore the request and access without authentication
        web.ignoring().antMatchers("/plugins/**"."**.js"."/img/**"."**.css"."/favicon.ico"."/401.html"."/404.html"); }}Copy the code
package com.ml.jkeep.internal.auth;

import com.alibaba.fastjson.JSON;
import com.ml.jkeep.common.constant.Common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/** * Authentication policy **@authorTan Liangzhong *@date 2019/6/20 13:24
 */
@Slf4j
@Component
public class JKeepAccessDecisionManager implements AccessDecisionManager {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        String requestUrl = filterInvocation.getRequestUrl();
        log.info("requestUrl : {} , configAttributes: {} , authentication: {}", requestUrl, JSON.toJSONString(configAttributes), JSON.toJSONString(authentication));
        if(! antPathMatcher.match(Common.LOGIN_PAGE_URL, requestUrl)) {if (authentication instanceof AnonymousAuthenticationToken) {
                throw new BadCredentialsException("Not logged in"); }}else {
            // The login page is accessible without authentication
            return;
        }
        // Verify that the user has access to the current address
        // configAttributes can access url roles
        / / authentication. GetAuthorities () all the user roles
        List<GrantedAuthority> isPermission = new ArrayList<>();
        configAttributes.forEach(configAttribute -> isPermission.addAll(authentication.getAuthorities().stream().filter(authority -> authority.getAuthority().equals(configAttribute.getAttribute())).collect(Collectors.toList())));
        if (isPermission.isEmpty()) {
            log.info("User permissions are insufficient to access!");
            throw new AccessDeniedException("User permissions are insufficient to access!"); }}@Override
    public boolean supports(ConfigAttribute attribute) {
        return false;
    }

    @Override
    public boolean supports(Class
        clazz) {
        return false; }}Copy the code
package com.ml.jkeep.internal.auth;

import com.alibaba.fastjson.JSON;
import com.ml.jkeep.common.constant.Common;
import com.ml.jkeep.jpa.system.vo.HrefPermissionVo;
import com.ml.jkeep.service.system.impl.HrefPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/** * Filter calls security metadata source **@authorTan Liangzhong *@dateTruly 2019/6/20 * /
@Slf4j
@Component
public class JKeepSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    @Autowired
    private HrefPermissionService hrefPermissionService;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        String requestUrl = filterInvocation.getRequestUrl();
        Set<HrefPermissionVo> allPer = hrefPermissionService.hrefPermission();
        Set<String> roles = new HashSet<>();
        for (HrefPermissionVo per : allPer) {
            if(antPathMatcher.match(per.getHref(), requestUrl)) { roles.add(per.getCode()); }}if(! CollectionUtils.isEmpty(roles)) { log.info("JKeepSecurityMetadataSource, requestUrl : {}, roles: {}", requestUrl, JSON.toJSONString(roles));
            return SecurityConfig.createList(roles.toArray(new String[0]));
        }
        log.info("JKeepSecurityMetadataSource, requestUrl : {}, roles: {}", requestUrl, Common.ROLE_DEFAULT);
        // If no permission control is configured for the URL, the default role can access the URL. That is, all users can access it
        return SecurityConfig.createList(Common.ROLE_DEFAULT);
    }


    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes(a) {
        return null;
    }

    @Override
    public boolean supports(Class
        clazz) {
        return false; }}Copy the code
package com.ml.jkeep.internal.auth;

import com.alibaba.fastjson.JSONObject;
import com.ml.jkeep.common.constant.Common;
import com.ml.jkeep.common.constant.ResultMsg;
import com.ml.jkeep.common.vo.RestVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/** * Access denied processing **@authorTan Liangzhong *@date2019/6/20 so * /
@Slf4j
@Component
public class JKeepAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        log.info(Parameter: {}, RequestURI: {}, RemoteUser: {}, RemoteAddr: {}, Message: {}",
                JSONObject.toJSONString(request.getParameterMap()), request.getRequestURI(),
                request.getRemoteUser(), request.getRemoteAddr(), accessDeniedException.getMessage());
        if(request.getHeader(Common.CSRF_TOKEN_KEY) ! =null) {
            / / Ajax to submit
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("application/json; charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.write(JSONObject.toJSONString(RestVo.FAIL(ResultMsg.ACCESS_DENIED)));
            out.flush();
            out.close();
        } else {
            // Form submission. Redirect page 401
            response.sendRedirect("/401.html"); }}}Copy the code
package com.ml.jkeep.jpa.system.entity.sys;

import com.ml.jkeep.common.constant.Common;
import com.ml.jkeep.jpa.system.vo.HrefPermissionVo;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.*;

/** * User authentication information - Entity **@authorTan Liangzhong *@date2019/6/20 11 * /
@Data
public class UserAuth implements UserDetails {

    private String username;
    private String password;
    private Set<HrefPermissionVo> hrefPer = new HashSet<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        hrefPer.forEach(per -> authorities.add(new SimpleGrantedAuthority(per.getCode())));
        // Default role
        authorities.add(new SimpleGrantedAuthority(Common.ROLE_DEFAULT));
        return authorities;
    }

    @Override
    public boolean isAccountNonExpired(a) {
        // Whether the TODO account has expired
        return true;
    }

    @Override
    public boolean isAccountNonLocked(a) {
        // Whether the TODO account is frozen
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired(a) {
        // Whether the TODO account password has expired
        return true;
    }

    @Override
    public boolean isEnabled(a) {
        // The TODO account is available
        return true; }}Copy the code
package com.ml.jkeep.service.system.impl;

import com.ml.jkeep.jpa.system.entity.sys.User;
import com.ml.jkeep.jpa.system.entity.sys.UserAuth;
import com.ml.jkeep.jpa.system.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/** * authentication -impl **@authorTan Liangzhong *@date2019/6/20 10:58 * /
@Slf4j
@Service
public class AuthServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;
    @Autowired
    private HrefPermissionService hrefPermissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("loadUserByUsername, username: {}", username);
        User user = userRepository.findByUsername(username);
        if (user == null) {
            log.error("Username does not exist, {}", username);
            throw new UsernameNotFoundException("Please enter the correct user name");
        }
        UserAuth userAuth = new UserAuth();
        userAuth.setUsername(user.getUsername());
        userAuth.setPassword(user.getPassword());
        userAuth.setHrefPer(hrefPermissionService.hrefPermission(user.getUserId()));
        returnuserAuth; }}Copy the code

Get the information about the currently logged in user

package com.ml.jkeep.internal.auth;

import com.alibaba.fastjson.JSONObject;
import com.ml.jkeep.jpa.system.entity.sys.UserAuth;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/** * Token holder (authentication related information) **@authorTan Liangzhong *@date2019/7/17 he * /
public class JKeepSecurityContextHolder {

    /**
     * 获取 sessionId
     *
     * @return sessionId
     */
    public static String getSessionId(a) {
        return getDetails().getString("sessionId");
    }

    /** * Stores additional details about the authentication request IP address, sessionId, etc. **@return {"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"DECEC3BE2FC3F2D2002624E155939F35"}
     */
    public static JSONObject getDetails(a) {
        return (JSONObject) SecurityContextHolder.getContext().getAuthentication().getDetails();
    }

    /** * get user information **@returnUser information */
    public static UserAuth getUserInfo(a) {
        return (UserAuth) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    /** * Whether you have logged in to **@returnWhether you have logged in */
    public static boolean isAuthenticated(a) {
        return SecurityContextHolder.getContext().getAuthentication().isAuthenticated();
    }

    /** * get the role ** held by the user@returnSet of roles */
    public static Set<String> getRoles(a) {
        return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getAuthorities()).orElse(newArrayList<>()).stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); }}Copy the code

2. SpringBoot + Thymeleaf + Spring Security

1.add Depending

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
Copy the code

Example 2.

<! DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> <head> <meta charset="utf-8"> <title>Demo</title> </head> <span SEC :authentication="name"></span><br/> <span SEC :authentication=" principo.authorities "></span> <div SEC :authorize="${hasRole('DEFAULT')}"> Is the role </div> <div SEC :authorize="${hasAuthority('DEMO')}"> </div> </body>Copy the code