从零搭建 Spring Boot 后端项目(四)

朱雀 2023-02-28 05:43 272阅读 0赞

简介

这一小节主要是,整合 Spring Security

步骤

  • 新建数据库与表

    1. Create DATABASE `backend_template`;
    2. USE backend_template;
    3. CREATE TABLE `user` (
    4. `id` bigint(11) NOT NULL AUTO_INCREMENT,
    5. `username` varchar(255) NOT NULL,
    6. `password` varchar(255) NOT NULL,
    7. PRIMARY KEY (`id`)
    8. );
    9. CREATE TABLE `role` (
    10. `id` bigint(11) NOT NULL AUTO_INCREMENT,
    11. `name` varchar(255) NOT NULL,
    12. PRIMARY KEY (`id`)
    13. );
    14. CREATE TABLE `user_role` (
    15. `user_id` bigint(11) NOT NULL,
    16. `role_id` bigint(11) NOT NULL
    17. );
    18. CREATE TABLE `role_permission` (
    19. `role_id` bigint(11) NOT NULL,
    20. `permission_id` bigint(11) NOT NULL
    21. );
    22. CREATE TABLE `permission` (
    23. `id` bigint(11) NOT NULL AUTO_INCREMENT,
    24. `url` varchar(255) NOT NULL,
    25. `name` varchar(255) NOT NULL,
    26. `description` varchar(255) NULL,
    27. `pid` bigint(11) NOT NULL,
    28. PRIMARY KEY (`id`)
    29. );
    30. INSERT INTO user (id, username, password) VALUES (1,'user','e10adc3949ba59abbe56e057f20f883e');
    31. INSERT INTO user (id, username , password) VALUES (2,'admin','e10adc3949ba59abbe56e057f20f883e');
    32. INSERT INTO role (id, name) VALUES (1,'USER');
    33. INSERT INTO role (id, name) VALUES (2,'ADMIN');
    34. INSERT INTO permission (id, url, name, pid) VALUES (1,'/user/common','common',0);
    35. INSERT INTO permission (id, url, name, pid) VALUES (2,'/user/admin','admin',0);
    36. INSERT INTO user_role (user_id, role_id) VALUES (1, 1);
    37. INSERT INTO user_role (user_id, role_id) VALUES (2, 1);
    38. INSERT INTO user_role (user_id, role_id) VALUES (2, 2);
    39. INSERT INTO role_permission (role_id, permission_id) VALUES (1, 1);
    40. INSERT INTO role_permission (role_id, permission_id) VALUES (2, 1);
    41. INSERT INTO role_permission (role_id, permission_id) VALUES (2, 2);
  • 修改application-dev.properties配置文件中连接数据库为backend_template,此时就可以把test数据库删了,因为第三篇还要连接所以之前没让删

    1. spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/backend_template?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
  • pom.xml中新增以下两个依赖

    1. <!-- Spring Security -->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-security</artifactId>
    5. <version>2.3.1.RELEASE</version>
    6. </dependency>
    7. <!-- thymeleaf -->
    8. <dependency>
    9. <groupId>org.springframework.boot</groupId>
    10. <artifactId>spring-boot-starter-thymeleaf</artifactId>
    11. <version>2.3.1.RELEASE</version>
    12. </dependency>
  • com.example.backend_template.entity下新建User类

    1. package com.example.backend_template.entity;
    2. import org.springframework.security.core.userdetails.UserDetails;
    3. import java.io.Serializable;
    4. import java.util.List;
    5. /**
    6. * @ClassName User
    7. * @Description
    8. * @Author L
    9. * @Date Create by 2020/6/28
    10. */
    11. public class User implements UserDetails, Serializable {
    12. private Long id;
    13. private String username;
    14. private String password;
    15. private List<Role> authorities;
    16. public Long getId() {
    17. return id;
    18. }
    19. public void setId(Long id) {
    20. this.id = id;
    21. }
    22. @Override
    23. public String getUsername() {
    24. return username;
    25. }
    26. public void setUsername(String username) {
    27. this.username = username;
    28. }
    29. @Override
    30. public String getPassword() {
    31. return password;
    32. }
    33. public void setPassword(String password) {
    34. this.password = password;
    35. }
    36. @Override
    37. public List<Role> getAuthorities() {
    38. return authorities;
    39. }
    40. public void setAuthorities(List<Role> authorities) {
    41. this.authorities = authorities;
    42. }
    43. /**
    44. * 用户帐号是否未过期
    45. *
    46. * @return
    47. */
    48. @Override
    49. public boolean isAccountNonExpired() {
    50. return true;
    51. }
    52. /**
    53. * 用户帐号是否未锁定
    54. *
    55. * @return
    56. */
    57. @Override
    58. public boolean isAccountNonLocked() {
    59. return true;
    60. }
    61. /**
    62. * 用户密码是否未过期
    63. *
    64. * @return
    65. */
    66. @Override
    67. public boolean isCredentialsNonExpired() {
    68. return true;
    69. }
    70. /**
    71. * 用户是否可用
    72. *
    73. * @return
    74. */
    75. @Override
    76. public boolean isEnabled() {
    77. return true;
    78. }
    79. }

    上面的 User 类实现了 UserDetails 接口,该接口是实现Spring Security 认证信息的核心接口。其中 getUsername 方法为 UserDetails 接口 的方法,这个方法返回 username,也可以是其他的用户信息,例如手机号、邮箱等。getAuthorities() 方法返回的是该用户设置的权限信息,在本实例中,从数据库取出用户的所有角色信息,权限信息也可以是用户的其他信息,不一定是角色信息。另外需要读取密码,最后几个方法一般情况下都返回 true,也可以根据自己的需求进行业务判断。

  • com.example.backend_template.entity下新建Role类

    1. package com.example.backend_template.entity;
    2. import org.springframework.security.core.GrantedAuthority;
    3. /**
    4. * @ClassName Role
    5. * @Description
    6. * @Author L
    7. * @Date Create by 2020/6/28
    8. */
    9. public class Role implements GrantedAuthority {
    10. private Long id;
    11. private String name;
    12. public Long getId() {
    13. return id;
    14. }
    15. public void setId(Long id) {
    16. this.id = id;
    17. }
    18. public String getName() {
    19. return name;
    20. }
    21. public void setName(String name) {
    22. this.name = name;
    23. }
    24. @Override
    25. public String getAuthority() {
    26. return name;
    27. }
    28. }

    Role 类实现了 GrantedAuthority 接口,并重写 getAuthority() 方法。权限点可以为任何字符串,不一定非要用角色名。
    AuthenticationManager会设置到一个GrantedAuthority列表到Authentication对象中保存,GrantedAuthority列表表示用户所具有的权限,AccessDecisionManager将从Authentication中获取用户的GrantedAuthority来鉴定用户是否具有访问权限。

  • com.example.backend_template.dao下新建UserDao接口

    1. package com.example.backend_template.dao;
    2. import com.example.backend_template.entity.User;
    3. import org.apache.ibatis.annotations.Mapper;
    4. import org.apache.ibatis.annotations.Param;
    5. /**
    6. * @ClassName UserDao
    7. * @Description
    8. * @Author L
    9. * @Date Create by 2020/6/28
    10. */
    11. @Mapper
    12. public interface UserDao {
    13. /**
    14. * 通过用户名加载用户
    15. *
    16. * @param userName
    17. * @return
    18. */
    19. User findByUsername(@Param("userName") String userName);
    20. }
  • resources/mapper下新建UserDao.xml文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.example.backend_template.dao.UserDao">
    6. <select id="findByUsername" parameterType="string" resultType="com.example.backend_template.entity.User">
    7. SELECT * FROM user u WHERE u.username = #{userName}
    8. </select>
    9. </mapper>
  • com.example.backend_template.dao下新建RoleDao接口

    1. package com.example.backend_template.dao;
    2. import com.example.backend_template.entity.Role;
    3. import org.apache.ibatis.annotations.Mapper;
    4. import org.springframework.data.repository.query.Param;
    5. import java.util.List;
    6. /**
    7. * @ClassName RoleDao
    8. * @Description
    9. * @Author L
    10. * @Date Create by 2020/6/28
    11. */
    12. @Mapper
    13. public interface RoleDao {
    14. /**
    15. * 通过用户ID查找用户角色
    16. *
    17. * @param userId
    18. * @return
    19. */
    20. List<Role> findByUserId(@Param("userId") Long userId);
    21. }
  • resources/mapper下新建RoleDao.xml文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.example.backend_template.dao.RoleDao">
    6. <select id="findByUserId" parameterType="Long" resultType="com.example.backend_template.entity.Role">
    7. SELECT * FROM role r WHERE r.id IN (SELECT ur.role_id FROM user_role ur WHERE ur.user_id = #{userId} )
    8. </select>
    9. </mapper>
  • com.example.backend_template.dao下新建PermissionDao接口

    1. package com.example.backend_template.dao;
    2. import org.apache.ibatis.annotations.Mapper;
    3. import java.util.List;
    4. import java.util.Map;
    5. /**
    6. * @ClassName PermissionDao
    7. * @Description
    8. * @Author L
    9. * @Date Create by 2020/6/29
    10. */
    11. @Mapper
    12. public interface PermissionDao {
    13. List<Map<String, String>> findRoleAndPermissions();
    14. }
  • resources/mappers下新建PermissionDao.xml文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.example.backend_template.dao.PermissionDao">
    6. <select id="findRoleAndPermissions" resultType="java.util.HashMap">
    7. SELECT R.name,P.url FROM role AS R LEFT JOIN role_permission RP ON R.id=RP.role_id LEFT JOIN permission AS P ON RP.permission_id=P.id
    8. </select>
    9. </mapper>
  • 并在BackendTemplateApplication启动类中添加@MapperScan("com.example.backend_template.dao")注解,如之前未删除则不再添加
  • com.example.backend_template.security下新建UserDetailsServiceImpl类

    1. package com.example.backend_template.security;
    2. import com.example.backend_template.dao.RoleDao;
    3. import com.example.backend_template.dao.UserDao;
    4. import com.example.backend_template.entity.Role;
    5. import com.example.backend_template.entity.User;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.security.core.userdetails.UserDetails;
    8. import org.springframework.security.core.userdetails.UserDetailsService;
    9. import org.springframework.security.core.userdetails.UsernameNotFoundException;
    10. import org.springframework.stereotype.Service;
    11. import java.util.List;
    12. /**
    13. * @ClassName UserDetailsService
    14. * @Description
    15. * @Author L
    16. * @Date Create by 2020/6/29
    17. */
    18. @Service
    19. public class UserDetailsServiceImpl implements UserDetailsService {
    20. @Autowired
    21. private UserDao userDao;
    22. @Autowired
    23. private RoleDao roleDao;
    24. @Override
    25. public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
    26. //查数据库,查找到用户名对应的所有角色,并注入user中
    27. User user = userDao.findByUsername(userName);
    28. if (user != null) {
    29. List<Role> roles = roleDao.findByUserId(user.getId());
    30. user.setAuthorities(roles);
    31. }
    32. return user;
    33. }
    34. }
  • com.example.backend_template.security下新建InvocationSecurityMetadataSourceServiceImpl类

    1. package com.example.backend_template.security;
    2. import com.example.backend_template.dao.PermissionDao;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.security.access.ConfigAttribute;
    5. import org.springframework.security.access.SecurityConfig;
    6. import org.springframework.security.web.FilterInvocation;
    7. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    8. import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    9. import org.springframework.stereotype.Component;
    10. import javax.servlet.http.HttpServletRequest;
    11. import java.util.*;
    12. /**
    13. * @ClassName InvocationSecurityMetadataSourceServiceImpl
    14. * @Description
    15. * @Author L
    16. * @Date Create by 2020/6/29
    17. */
    18. @Component
    19. public class InvocationSecurityMetadataSourceServiceImpl implements FilterInvocationSecurityMetadataSource {
    20. @Autowired
    21. private PermissionDao permissionDao;
    22. /**
    23. * 每一个资源所需要的角色 Collection<ConfigAttribute>决策器会用到
    24. */
    25. private static HashMap<String, Collection<ConfigAttribute>> map = null;
    26. /**
    27. * 返回请求的资源需要的角色
    28. *
    29. * @param o
    30. * @return
    31. * @throws IllegalArgumentException
    32. */
    33. @Override
    34. public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
    35. //object 中包含用户请求的request 信息
    36. HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
    37. for (Iterator<String> it = map.keySet().iterator(); it.hasNext(); ) {
    38. String url = it.next();
    39. if (new AntPathRequestMatcher(url).matches(request)) {
    40. return map.get(url);
    41. }
    42. }
    43. return null;
    44. }
    45. @Override
    46. public Collection<ConfigAttribute> getAllConfigAttributes() {
    47. //初始化 所有资源 对应的角色
    48. loadResourceDefine();
    49. return null;
    50. }
    51. @Override
    52. public boolean supports(Class<?> aClass) {
    53. return true;
    54. }
    55. /**
    56. * 初始化 所有资源 对应的角色
    57. */
    58. public void loadResourceDefine() {
    59. map = new HashMap<>(16);
    60. //查出结果为角色和对应URL的集合
    61. List<Map<String, String>> roleAndPermissions = permissionDao.findRoleAndPermissions();
    62. //某个资源可以被哪些角色访问
    63. for (Map<String, String> roleAndPermission : roleAndPermissions) {
    64. String roleName = roleAndPermission.get("name");
    65. String url = roleAndPermission.get("url");
    66. ConfigAttribute role = new SecurityConfig(roleName);
    67. if (map.containsKey(url)) {
    68. map.get(url).add(role);
    69. } else {
    70. List<ConfigAttribute> list = new ArrayList<>();
    71. list.add(role);
    72. map.put(url, list);
    73. }
    74. }
    75. }
    76. }

    InvocationSecurityMetadataSourceServiceImpl 类实现了 FilterInvocationSecurityMetadataSource,FilterInvocationSecurityMetadataSource 的作用是用来储存请求与权限的对应关系。

    FilterInvocationSecurityMetadataSource接口有3个方法:

    boolean supports(Class<?> clazz):指示该类是否能够为指定的方法调用或Web请求提供ConfigAttributes。
    Collection getAllConfigAttributes():Spring容器启动时自动调用, 一般把所有请求与权限的对应关系也要在这个方法里初始化, 保存在一个属性变量里。
    Collection getAttributes(Object object):当接收到一个http请求时, filterSecurityInterceptor会调用的方法. 参数object是一个包含url信息的HttpServletRequest实例. 这个方法要返回请求该url所需要的所有权限集合

  • com.example.backend_template.security下新建AccessDecisionManagerImpl类

    1. package com.example.backend_template.security;
    2. import org.springframework.security.access.AccessDecisionManager;
    3. import org.springframework.security.access.AccessDeniedException;
    4. import org.springframework.security.access.ConfigAttribute;
    5. import org.springframework.security.authentication.InsufficientAuthenticationException;
    6. import org.springframework.security.core.Authentication;
    7. import org.springframework.security.core.GrantedAuthority;
    8. import org.springframework.stereotype.Component;
    9. import java.util.Collection;
    10. import java.util.Iterator;
    11. /**
    12. * @ClassName AccessDecisionManagerImpl
    13. * @Description
    14. * @Author L
    15. * @Date Create by 2020/6/30
    16. */
    17. @Component
    18. public class AccessDecisionManagerImpl implements AccessDecisionManager {
    19. /**
    20. * 通过传递的参数来决定用户是否有访问对应受保护对象的权限
    21. *
    22. * @param authentication 包含了当前的用户信息,包括拥有的权限。这里的权限来源就是前面登录时UserDetailsService中设置的authorities。
    23. * @param object 就是FilterInvocation对象,可以得到request等web资源
    24. * @param configAttributes configAttributes是本次访问需要的权限
    25. */
    26. @Override
    27. public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
    28. if (configAttributes == null || configAttributes.size() <= 0) {
    29. return;
    30. } else {
    31. String needRole;
    32. for (Iterator<ConfigAttribute> iterator = configAttributes.iterator(); iterator.hasNext(); ) {
    33. needRole = iterator.next().getAttribute();
    34. for (GrantedAuthority ga : authentication.getAuthorities()) {
    35. if (needRole.trim().equals(ga.getAuthority().trim())) {
    36. return;
    37. }
    38. }
    39. }
    40. }
    41. throw new AccessDeniedException("当前没有访问权限!");
    42. }
    43. /**
    44. * 表示此AccessDecisionManager是否能够处理传递的ConfigAttribute呈现的授权请求
    45. *
    46. * @param configAttribute
    47. * @return
    48. */
    49. @Override
    50. public boolean supports(ConfigAttribute configAttribute) {
    51. return true;
    52. }
    53. /**
    54. * 表示当前AccessDecisionManager实现是否能够为指定的安全对象(方法调用或Web请求)提供访问控制决策
    55. *
    56. * @param aClass
    57. * @return
    58. */
    59. @Override
    60. public boolean supports(Class<?> aClass) {
    61. return true;
    62. }
    63. }

    AccessDecisionManagerImpl 类实现了AccessDecisionManager接口,AccessDecisionManager是由AbstractSecurityInterceptor调用的,它负责鉴定用户是否有访问对应资源(方法或URL)的权限。

  • com.example.backend_template.security下新建FilterSecurityInterceptorImpl类

    1. package com.example.backend_template.security;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.security.access.SecurityMetadataSource;
    4. import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
    5. import org.springframework.security.access.intercept.InterceptorStatusToken;
    6. import org.springframework.security.web.FilterInvocation;
    7. import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    8. import org.springframework.stereotype.Component;
    9. import javax.servlet.*;
    10. import java.io.IOException;
    11. /**
    12. * @ClassName FilterSecurityInterceptorImpl
    13. * @Description
    14. * @Author L
    15. * @Date Create by 2020/6/30
    16. */
    17. @Component
    18. public class FilterSecurityInterceptorImpl extends AbstractSecurityInterceptor implements Filter {
    19. @Autowired
    20. private FilterInvocationSecurityMetadataSource securityMetadataSource;
    21. @Autowired
    22. public void setAccessDecisionManagerImpl(AccessDecisionManagerImpl accessDecisionManagerImpl) {
    23. super.setAccessDecisionManager(accessDecisionManagerImpl);
    24. }
    25. @Override
    26. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    27. FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
    28. invoke(fi);
    29. }
    30. public void invoke(FilterInvocation fi) throws IOException, ServletException {
    31. InterceptorStatusToken token = super.beforeInvocation(fi);
    32. try {
    33. //执行下一个拦截器
    34. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
    35. } finally {
    36. super.afterInvocation(token, null);
    37. }
    38. }
    39. @Override
    40. public Class<?> getSecureObjectClass() {
    41. return FilterInvocation.class;
    42. }
    43. @Override
    44. public SecurityMetadataSource obtainSecurityMetadataSource() {
    45. return this.securityMetadataSource;
    46. }
    47. }

    每种受支持的安全对象类型(方法调用或Web请求)都有自己的拦截器类,它是AbstractSecurityInterceptor的子类,AbstractSecurityInterceptor 是一个实现了对受保护对象的访问进行拦截的抽象类。
    AbstractSecurityInterceptor中的方法说明:
    beforeInvocation()方法实现了对访问受保护对象的权限校验,内部用到了AccessDecisionManager和AuthenticationManager;
    finallyInvocation()方法用于实现受保护对象请求完毕后的一些清理工作,主要是如果在beforeInvocation()中改变了SecurityContext,则在finallyInvocation()中需要将其恢复为原来的SecurityContext,该方法的调用应当包含在子类请求受保护资源时的finally语句块中。
    afterInvocation()方法实现了对返回结果的处理,在注入了AfterInvocationManager的情况下默认会调用其decide()方法。
    了解了AbstractSecurityInterceptor,就应该明白了,我们自定义FilterSecurityInterceptorImpl就是想使用我们之前自定义的 AccessDecisionManager 和 securityMetadataSource。

  • com.example.backend_template.security下新建SecurityConfig类

    1. package com.example.backend_template.security;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    6. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    7. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    8. import org.springframework.security.crypto.password.PasswordEncoder;
    9. import org.springframework.util.DigestUtils;
    10. /**
    11. * @ClassName SecurityConfig
    12. * @Description
    13. * @Author L
    14. * @Date Create by 2020/6/30
    15. */
    16. @Configuration
    17. @EnableWebSecurity
    18. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    19. @Autowired
    20. private UserDetailsServiceImpl userService;
    21. @Autowired
    22. public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    23. //校验用户
    24. auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {
    25. //对密码进行加密
    26. @Override
    27. public String encode(CharSequence charSequence) {
    28. return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    29. }
    30. //对密码进行判断匹配
    31. @Override
    32. public boolean matches(CharSequence charSequence, String s) {
    33. String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    34. boolean res = s.equals(encode);
    35. return res;
    36. }
    37. });
    38. }
    39. @Override
    40. protected void configure(HttpSecurity http) throws Exception {
    41. http.authorizeRequests()
    42. .antMatchers("/", "/index", "/login", "/login-error", "/401").permitAll()
    43. .anyRequest().authenticated()
    44. .and()
    45. .formLogin().loginPage("/login").failureUrl("/login-error")
    46. .and()
    47. .exceptionHandling().accessDeniedPage("/401");
    48. http.logout().logoutSuccessUrl("/");
    49. }
    50. }

    @EnableWebSecurity注解以及WebSecurityConfigurerAdapter一起配合提供基于web的security。自定义类继承了WebSecurityConfigurerAdapter来重写了一些方法来指定一些特定的Web安全设置

测试

  • com.example.backend_template.controller下新建SecurityController类

    1. package com.example.backend_template.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.ui.Model;
    4. import org.springframework.web.bind.annotation.GetMapping;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. /**
    7. * @ClassName SecurityController
    8. * @Description
    9. * @Author L
    10. * @Date Create by 2020/6/30
    11. */
    12. @Controller
    13. public class SecurityController {
    14. @RequestMapping("/")
    15. public String root() {
    16. return "redirect:/index";
    17. }
    18. @RequestMapping("/index")
    19. public String index() {
    20. return "index";
    21. }
    22. @RequestMapping("/login")
    23. public String login() {
    24. return "login";
    25. }
    26. @RequestMapping("/login-error")
    27. public String loginError(Model model) {
    28. model.addAttribute( "loginError" , true);
    29. return "login";
    30. }
    31. @GetMapping("/401")
    32. public String accessDenied() {
    33. return "401";
    34. }
    35. @GetMapping("/user/common")
    36. public String common() {
    37. return "user/common";
    38. }
    39. @GetMapping("/user/admin")
    40. public String admin() {
    41. return "user/admin";
    42. }
    43. }
  • resources/templates下新建401.html

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>401 page</title>
    6. </head>
    7. <body>
    8. <div>
    9. <div>
    10. <h2>权限不够</h2>
    11. <p>拒绝访问!</p>
    12. </div>
    13. </div>
    14. </body>
    15. </html>
  • resources/templates下新建index.html

    1. <!DOCTYPE html>
    2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <head>
    5. <meta charset="UTF-8">
    6. <title>首页</title>
    7. </head>
    8. <body>
    9. <h2>page list</h2>
    10. <a href="/user/common">common page</a>
    11. <br/>
    12. <a href="/user/admin">admin page</a>
    13. <br/>
    14. <form th:action="@{/logout}" method="post">
    15. <input type="submit" class="btn btn-primary" value="注销"/>
    16. </form>
    17. </body>
    18. </html>
  • resources/templates下新建login.html

    1. <!DOCTYPE html>
    2. <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>登录</title>
    6. </head>
    7. <body>
    8. <h1>Login page</h1>
    9. <p th:if="${loginError}" class="error">用户名或密码错误</p>
    10. <form th:action="@{/login}" method="post">
    11. <label for="username">用户名</label>:
    12. <input type="text" id="username" name="username" autofocus="autofocus" />
    13. <br/>
    14. <label for="password">密 码</label>:
    15. <input type="password" id="password" name="password" />
    16. <br/>
    17. <input type="submit" value="登录" />
    18. </form>
    19. <p><a href="/index" th:href="@{/index}"></a></p>
    20. </body>
    21. </html>
  • 首先在resources/templates下新建user文件夹,然后在resources/templates/user下新建admin.html

    1. <!DOCTYPE html>
    2. <head>
    3. <meta charset="UTF-8">
    4. <title>admin page</title>
    5. </head>
    6. <body>
    7. success admin page!!!
    8. </body>
    9. </html>
  • resources/templates/user下新建common.html

    1. <!DOCTYPE html>
    2. <head>
    3. <meta charset="UTF-8">
    4. <title>common page</title>
    5. </head>
    6. <body>
    7. success common page!!!
    8. </body>
    9. </html>
  • 启动项目,并用浏览器访问 http://localhost:8080/index 并选择 common page
    在这里插入图片描述
  • 输入用户名:”user“,密码:”123456“,出现以下界面,说明登陆成功
    在这里插入图片描述
  • 接着访问 http://localhost:8080/user/admin ,会出现以下界面,因为user用户没有权力访问/user/admin界面,必须用admin帐号才能访问
    在这里插入图片描述
    如成功出现以上所有结果,则Spring Security 整合成功

项目地址

项目介绍:从零搭建 Spring Boot 后端项目
代码地址:https://github.com/xiaoxiamo/backend-template

下一篇

五、整合Swagger2

发表评论

表情:
评论列表 (有 0 条评论,272人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Spring Boot 项目

    简介 一个基于 Spring Boot 的后端开发模板,主要用于减少平时重复的工作量,以及使开发有良好的开发规范,主要功能包括但不限于权限管理、在线接口文档、日志记录、单