SpringBoot:集成SpringSecurity

一、创建项目,添加模块:

二、创建实体类SysUser,该类必须继承SpringSecurity提供的UserDetails类:

package com.example.entity;

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

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class SysUser implements UserDetails {
    
    private static final long serialVersionUID = -1533292207697155409L;
    
    private String id;
    private String username;
    private String password;
    private Integer status;
    private List<SysRole> sysRoles;
    private List<SysPrivilegeMenu> sysPrivilegeMenus;
    private List<SysPrivilegeButton> sysPrivilegeButtons;

    /**
     * 当用户登录成功后,SpringSecurity会调用此方法获取已登录用户的权限列表
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if (this.sysPrivilegeMenus != null) {
            this.sysPrivilegeMenus.forEach(sysPrivilegeMenu -> {
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(sysPrivilegeMenu.buildPrivilegeString());
                grantedAuthorities.add(grantedAuthority);
            });
        }
        if (this.sysPrivilegeButtons != null) {
            this.sysPrivilegeButtons.forEach(sysPrivilegeButton -> {
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(sysPrivilegeButton.buildPrivilegeString());
                grantedAuthorities.add(grantedAuthority);
            });
        }
        return grantedAuthorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        if (this.status == -3) {
            return false;
        }
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        if (this.status == -2) {
            return false;
        }
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        if (this.status == -4) {
            return false;
        }
        return true;
    }

    @Override
    public boolean isEnabled() {
        if (this.status == -1) {
            return false;
        }
        return true;
    }

    public String getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public List<SysRole> getSysRoles() {
        return sysRoles;
    }

    public void setSysRoles(List<SysRole> sysRoles) {
        this.sysRoles = sysRoles;
    }

    public List<SysPrivilegeMenu> getSysPrivilegeMenus() {
        return sysPrivilegeMenus;
    }

    public void setSysPrivilegeMenus(List<SysPrivilegeMenu> sysPrivilegeMenus) {
        this.sysPrivilegeMenus = sysPrivilegeMenus;
    }

    public List<SysPrivilegeButton> getSysPrivilegeButtons() {
        return sysPrivilegeButtons;
    }

    public void setSysPrivilegeButtons(List<SysPrivilegeButton> sysPrivilegeButtons) {
        this.sysPrivilegeButtons = sysPrivilegeButtons;
    }
}

三、创建服务类SysUserDetailsService,该类必须继承SpringSecurity提供的UserDetailsService类:

package com.example.service;

import java.util.List;

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;

import com.example.dao.SysUserDAO;
import com.example.entity.SysPrivilegeButton;
import com.example.entity.SysPrivilegeMenu;
import com.example.entity.SysRole;
import com.example.entity.SysUser;

@Service
public class SysUserDetailsService implements UserDetailsService {
    
    @Autowired
    private SysUserDAO sysUserDAO;

    /**
     * 当登录时,SpringSecurity会调用此方法获取将要认证的用户信息。这里有个问题,只要此用户存在,就会去加载用户的角色权限信息,但此时如果用户是禁用状态,这一步就是多余的,这是一个框架不完善的地方
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser dbSysUser = sysUserDAO.findByUsername(username);
        if (dbSysUser == null) {
            throw new UsernameNotFoundException("User does not exist");
        }
        dbSysUser = loadRolesAndPrivileges(dbSysUser);
        return dbSysUser;
    }
    
    public SysUser loadRolesAndPrivileges(SysUser loginedUser) {
        List<SysRole> sysRoles = sysUserDAO.findRolesById(loginedUser.getId());
        List<SysPrivilegeMenu> sysPrivilegeMenus = sysUserDAO.findPrivilegeMenusById(loginedUser.getId());
        List<SysPrivilegeButton> sysPrivilegeButtons = sysUserDAO.findPrivilegeButtonsById(loginedUser.getId());
        loginedUser.setSysRoles(sysRoles);
        loginedUser.setSysPrivilegeMenus(sysPrivilegeMenus);
        loginedUser.setSysPrivilegeButtons(sysPrivilegeButtons);
        return loginedUser;
    }

}

四、WebMvcConfig.java的配置如下:

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addRedirectViewController("/", "/main");
        registry.addViewController("/auth/login").setViewName("login");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
        .addResourceHandler("/static/**")
        .addResourceLocations("classpath:/static/")
        .setCachePeriod(31556926);
    }

}

五、WebSecurityConfig.java的配置如下:

package com.example.config;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.cuckoo.universal.utils.web.ResponseEntity;
import org.cuckoo.universal.utils.web.ResponseUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;

import com.example.entity.SysUser;
import com.example.service.SysUserDetailsService;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SysUserDetailsService sysUserDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(sysUserDetailsService);
        authenticationProvider.setHideUserNotFoundExceptions(false);
        authenticationProvider.setPasswordEncoder(new PasswordEncoder() {
            /* 密码加密逻辑 */
            @Override
            public String encode(CharSequence charSequence) {
                PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                return passwordEncoder.encode(charSequence);
            }
            /* 密码比对逻辑 */
            @Override
            public boolean matches(CharSequence charSequence, String dbPassword) {
                PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
                return passwordEncoder.matches(charSequence, dbPassword);
            }
        });
        auth.authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /* 禁用CSRF防护 */
        http.csrf().disable();
        /* 禁用会话管理,比如当使用token认证时 */
        //http.sessionManagement().disable();
        /* 访问路径控制 */
        http
        .authorizeRequests()
        .antMatchers("/auth/login", "/auth/login/verify").permitAll()
        .antMatchers("/sys/user/**").hasAuthority("MENU-系统管理-用户管理")
        .antMatchers("/sys/role/**").hasAuthority("MENU-系统管理-角色管理")
        .anyRequest().authenticated();
        /* 登录处理 */
        http
        .formLogin()
        .loginPage("/auth/login")
        .loginProcessingUrl("/auth/login/verify")
        .usernameParameter("username")
        .passwordParameter("password")
        .successHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                SysUser loginedUser = (SysUser) authentication.getPrincipal();
                String msg = ResponseEntity.create().success().message("登录成功").put("loginedUser", loginedUser).asJSON();
                ResponseUtils.writeJson(msg, response);
            }
        })
        .failureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
                System.out.println("Reasons for login failure: "+authenticationException.getMessage());
                String message = null;
                if (authenticationException.fillInStackTrace() instanceof UsernameNotFoundException) {
                    message = "用户不存在";
                } else if (authenticationException.fillInStackTrace() instanceof BadCredentialsException) {
                    message = "输入密码错误";
                } else if (authenticationException.fillInStackTrace() instanceof DisabledException) {
                    message = "该用户帐号已被禁用";
                } else if (authenticationException.fillInStackTrace() instanceof LockedException) {
                    message = "该用户帐号已被锁定";
                } else if (authenticationException.fillInStackTrace() instanceof AccountExpiredException) {
                    message = "该用户帐号已过期";
                } else if (authenticationException.fillInStackTrace() instanceof CredentialsExpiredException) {
                    message = "该用户凭证已过期";
                }
                String msg = ResponseEntity.create().failure().message(message).asJSON();
                ResponseUtils.writeJson(msg, response);
            }
        });
        /* 注销处理 */
        http
        .logout()
        .logoutUrl("/auth/logout")
        .addLogoutHandler(new LogoutHandler() {
            @Override
            public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
                try {
                    String msg = ResponseEntity.create().success().message("注销成功").asJSON();
                    ResponseUtils.writeJson(msg, response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        /* 放行静态资源(此处只能表明SpringSecurity不拦截,仍需要配置静态资源访问路径) */
        web.ignoring().antMatchers("/static/**");
    }
}

六、application.properties的配置如下(此处并没有涉及到SpringSecurity的配置信息):

###################################
## SPRING MVC (WebMvcProperties) ##
###################################
# Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.throw-exception-if-no-handler-found = true

####################################################
## SPRING RESOURCES HANDLING (ResourceProperties) ##
####################################################
# Whether to enable default resource handling.
spring.resources.add-mappings = false

############################################
## THYMELEAF (ThymeleafAutoConfiguration) ##
############################################
# Whether to enable Thymeleaf view resolution for Web frameworks.
spring.thymeleaf.enabled = true
# Whether to enable template caching.
spring.thymeleaf.cache = false

七、其它文件代码参考

1、登录页面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form id="LoginForm" th:action="@{/auth/login/verify}" method="post">
        <input type="text" name="username" value="" autocomplete="off"/>
        <input type="password" name="password" value="" autocomplete="off"/>
        <button type="button" onclick="login()">登录</button>
    </form>
    <!-- Script Files -->
    <script type="text/javascript" th:src="@{/static/thirdparty/jquery/jquery-3.3.1.min.js}"></script>
    <script type="text/javascript" th:src="@{/static/thirdparty/jquery/form/jquery.form.min.js}"></script>
    <script type="text/javascript">
        function login(){
            $("#LoginForm").ajaxSubmit(function(data){
                alert(data.message);
                if(data.success){
                    window.location.href = "/main";
                }
            });
        }
    </script>
</body>
</html>

2、主页面控制器:

package com.example.controller;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.entity.SysUser;

@Controller
@RequestMapping("/main")
public class MainController {

    @GetMapping
    public String main(Model model) {
        
        /* 获取当前已登录用户,相当于Shiro中的SecurityUtils.getSubject(),随处可用 */
        SysUser loginedUser = (SysUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        Collection<? extends GrantedAuthority> grantedAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        
        System.out.println("登录用户:"+loginedUser.getUsername()+",已分配的权限:"+grantedAuthorities);
        model.addAttribute("loginedUser", loginedUser);
        model.addAttribute("grantedAuthorities", grantedAuthorities);
        return "main";
    }
}

3、主页面:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>main</title>
</head>
<body>
    <h5 th:text="${'LoginedUser: '+loginedUser.username}"></h5>
    <h5>GrantedAuthorities:</h5>
    <ul th:each="grantedAuthority: ${grantedAuthorities}">
        <li th:text="${grantedAuthority}"></li>
    </ul>
    <button type="button" onclick="logout()">注销</button>
    <!-- Script Files -->
    <script type="text/javascript" th:src="@{/static/thirdparty/jquery/jquery-3.3.1.min.js}"></script>
    <script type="text/javascript" th:src="@{/static/thirdparty/jquery/form/jquery.form.min.js}"></script>
    <script type="text/javascript">
        function logout(){
            $.ajax({
                "url": "/auth/logout",
                "method": "post",
                "success": function(data){
                    alert(data.message);
                    if(data.success){
                        window.location.href = "/auth/login";
                    }
                }
            });
        }
    </script>
</body>
</html>

八、项目结构参考:

 

posted @ 2019-02-15 12:26  cuckoo20100401  阅读(243)  评论(0)    收藏  举报