Spring Security進階

China熊孩子 發表於 2021-09-05
Spring

Spring Security進階

1.連線資料庫進行資料的驗證

Spring Security進行身份驗證或者許可權控制時,使用者名稱和密碼應該要和資料庫的進行比較才行,使用者的各種資訊我們從資料庫中去獲取,不用自己在程式碼或者配置檔案中寫。

案例

1)建立專案

自己建立一個Maven專案

2)匯入依賴

<parent>
    <!--Spring boot-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-parent</artifactId>
    <version>2.0.6.RELEASE</version>
  </parent>

  <dependencies>
    <!--Spring boot Web-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--Spring Security-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!--mysql驅動-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!--資料庫連線框架JPA-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
  </dependencies>

3)建立啟動類

@SpringBootApplication
public class SecurityApplication2 {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication2.class, args);
    }
}

4)配置檔案

在專案的resources檔案的目錄下建立一個配置檔案:application.properties

#連線資料庫
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/你的資料庫?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=使用者名稱
spring.datasource.password=密碼
#資料庫表的生成
spring.jpa.generate-ddl=true
#顯示執行的sql語句
spring.jpa.show-sql=true
#使用的資料庫
spring.jpa.database=mysql

5)建立類

來建立一個實體層類、dao層介面、service層、控制層

實體類

//這個註解表示這個是實體類,對應資料庫中的表,預設實體名就是類名
@Entity
public class UserInfo {

    //這個屬性的表中的主鍵
    @Id
    //主鍵的生成策略,IDENTITY 主鍵自增長
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //使用者名稱
    private String username;
    //密碼
    private String password;
    //角色
    private String roles;

    public Long getId() {
        return id;
    }

    public void setId(Long 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 String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }
}

dao層介面

/*
* 繼承JpaRepository<UserInfo,Long>介面,泛型一個是實體類,一個是主鍵型別
* */
public interface UserInfoDao extends JpaRepository<UserInfo,Long> {
    //根據使用者名稱查詢使用者
    //(溫馨提示:想要查詢的話方法名要為 findBy屬性欄位  刪除的話 deleteBy屬性欄位 ...以此類推這樣才不報錯)
    UserInfo findByUsername(String username);
}

service層

public interface UserInfoService {
    //根據使用者名稱查詢使用者
    UserInfo findByUsername(String username);
}
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;

    //根據使用者名稱返回使用者
    @Override
    public UserInfo findByUsername(String username) {
        UserInfo user = userInfoDao.findByUsername(username);
        return user;
    }
}

controller層

@RestController
@RequestMapping("/userInfo")
public class UserInfoController {

    @RequestMapping("common")
    //方法執行前驗證使用者是否有該角色
    @PreAuthorize(value = "hasAnyRole('normal','admin')")
    public String commonUserInfo() {
        return "==測試資料庫  有兩個角色==";
    }

    @RequestMapping("admin")
    //方法執行前驗證是否有該角色
    @PreAuthorize(value = "hasAnyRole('admin')")
    public String adminUserInfo() {
        return "==測試資料庫  有一個角色==";
    }
}

這些準備工作都做完之後,我們可以往資料庫中插入一些資料,建立一個類,用來往資料庫中新增資料的。

@Component
public class InitJdbc {

    @Autowired
    private UserInfoDao userInfoDao;

    //Java自帶的註解,程式啟動的時候執行該方法,每啟動一次執行一次,
    //插入成功了,再啟動專案的時候不想再重複插入,可以把@PostConstruct註釋掉
    @PostConstruct
    public void init() {
        //密碼要加密
        PasswordEncoder pe = new BCryptPasswordEncoder();

        //新增一個使用者
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("wangwu");
        userInfo.setPassword(pe.encode("123456"));
        userInfo.setRoles("normal");
        userInfoDao.save(userInfo);

        //新增一個使用者
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setUsername("admin");
        userInfo1.setPassword(pe.encode("admin"));
        userInfo1.setRoles("admin");
        userInfoDao.save(userInfo1);

    }
}

現在就可以啟動專案看看資料庫中是否插入成功資料了。

接下來做許可權的驗證==========

來寫一個類,實現UserDetailService並實現它的方法

//把類交給spring容器管理
@Component
public class SecurityDetail implements UserDetailsService {

    @Autowired
    private UserInfoDao userInfoDao;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        UserInfo userInfo = null;

        //UserDetails介面的實現類
        User user = null;
		//判斷使用者名稱不為空
        if (s != null) {
            //根據使用者名稱查詢使用者
            userInfo = userInfoDao.findByUsername(s);
            //判斷使用者不為空
            if (userInfo != null) {
				//User類的第三個引數是集合,所有建立一個集合,獲取使用者的角色
                List<GrantedAuthority> list = new ArrayList<>();
                //獲取使用者的角色,獲取到的角色開頭一定要以"ROLE_"開頭
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + userInfo.getRoles());
                list.add(authority);
                //User的構造器要返回三個引數:使用者名稱、密碼、集合的角色
                user = new User(userInfo.getUsername(), userInfo.getPassword(), list);
            }
        }

        return user;
    }
}

再寫一個配置類,解析密碼的加密方式

@Configuration
@EnableWebSecurity
//開啟方法級別的驗證
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityDetail securityDetail;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //密碼加密的方式
        auth.userDetailsService(securityDetail).passwordEncoder(new BCryptPasswordEncoder());

    }
}

現在可以進行測試了,啟動專案,訪問控制層的方法,只有方法上標記的角色才能訪問該方法

2.認證和授權

authentication:認證,認證訪問的使用者是不是有效的使用者,他是誰。

authorization:授權,訪問的使用者在系統中能幹什麼

RBAC:基於角色的訪問控制(Role-Based Access Control),使用者屬於某個角色,而角色擁有某些許可權。

許可權:能對資源進行操作,比如增刪改查

角色:自定義的,表示許可權的聚合,一個角色可以有多個許可權。

舉例說明

設計角色:經理具有資料的修改、刪除、檢視等;員工只能檢視資料。

一個公司中如果想把一個使用者設定為經理,只需把他設定為經理這個角色,他就能有修改、刪除、檢視等操作了,如果公司新來普通員工,只需把他加入到員工這個角色裡面就好了。這樣,想讓什麼使用者使用者什麼許可權,只需把他加入到相應的角色裡就OK了。

UserDetailService:這是一個介面,裡面只有一個方法UserDetails loadUserByUsername(String var1),是根據使用者名稱來獲取資料庫中資訊的

主要的實現有:

InMemoryUserDetailsManager在記憶體中維護使用者資訊的,使用很方便,可是資料不是持久的

JdbcUserDetailsManager對資料庫資訊進行操作的,底層是基於jdbcTemplate的,可以使用這個類的方法來運算元據庫資料。

UserDetails:提供使用者資訊的核心介面

// 許可權的集合
Collection<? extends GrantedAuthority> getAuthorities();
//獲取密碼
String getPassword();
//獲取使用者名稱
String getUsername();
//使用者是否存在
boolean isAccountNonExpired();
//使用者是否鎖定
boolean isAccountNonLocked();
//證照是否過期
boolean isCredentialsNonExpired();
//賬戶是否啟用 
boolean isEnabled();

UserDetails有一個實現類User

//他有兩個構造器,引數和UserDetails的欄位屬性一樣
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
   this(username, password, true, true, true, true, authorities);
}

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) {
       this.username = username;
       this.password = password;
       this.enabled = enabled;
       this.accountNonExpired = accountNonExpired;
       this.credentialsNonExpired = credentialsNonExpired;
       this.accountNonLocked = accountNonLocked;
       this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
   } else {
       throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
   }
}

2.1設定表

基於RBAC設定三張表,使用者表,角色表,使用者和角色的關聯表 密碼明文分別是 123 456 admin



-- 角色表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` int(11) NOT NULL,
  `rolename` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名稱',
  `rolememo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'USER', '普通使用者');
INSERT INTO `sys_role` VALUES (2, 'READ', '只讀');
INSERT INTO `sys_role` VALUES (3, 'ADMIN', '管理員');

SET FOREIGN_KEY_CHECKS = 1;


-- 使用者表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `realname` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真實名字',
  `isenable` int(11) NULL DEFAULT NULL COMMENT '是否開啟認證',
  `islock` int(11) NULL DEFAULT NULL COMMENT '是否鎖定',
  `isexpire` int(11) NULL DEFAULT NULL,
  `incredentials` int(255) NULL DEFAULT NULL COMMENT '是否過期',
  `createtime` date NULL DEFAULT NULL,
  `logintime` date NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user 密碼明文分別是  123   456   admin
-- ----------------------------
INSERT INTO `sys_user` VALUES (6, 'zs', '$2a$10$EGMo2XSdh49cDgXa0OzXYu36HfNssUf7zUDaNIz83AgWveA3GORYq', '張三', 1, 1, 1, 1, '2021-09-02', '2021-09-02');
INSERT INTO `sys_user` VALUES (7, 'lisi', '$2a$10$r9iLBYZzIIt/gyOngvPnZOBZaP4EW58etU1tLPoEh7hlYpydIaM6u', '李四', 1, 1, 1, 1, '2021-09-02', '2021-09-02');
INSERT INTO `sys_user` VALUES (8, 'admin', '$2a$10$P.I3zf7bEAmLmlSwaDOdMOdrxEyTT1QvbqfKC5YGQ7zHk5zUR/dCG', '管理員', 1, 1, 1, 1, '2021-09-02', '2021-09-02');

SET FOREIGN_KEY_CHECKS = 1;

--關聯表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `userid` int(11) NOT NULL,
  `roleid` int(11) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (6, 1);
INSERT INTO `sys_user_role` VALUES (7, 2);
INSERT INTO `sys_user_role` VALUES (8, 1);
INSERT INTO `sys_user_role` VALUES (8, 3);

SET FOREIGN_KEY_CHECKS = 1;

2.2建立專案

2.3 匯入依賴

把上一個專案的依賴匯入進來,再加多一個spring整合mybatis的包

 <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

2.4建立相關類

2.4.1實體類

建立User和Role的實體類,要繼承UserDetails

public class SysUser implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private String realName;
    private boolean isEnable;
    private boolean isExpired;
    private boolean isLock;
    private boolean isCredentials;

    private List<GrantedAuthority> grantedAuthorities;

    private Date createTime;
    private Date loginTime;

    public SysUser() {
    }

    public SysUser(String username, String password, String realName,
                   boolean isEnable, boolean isExpired, boolean isLock,
                   boolean isCredentials, List<GrantedAuthority> grantedAuthorities,
                   Date createTime, Date loginTime) {
        this.username = username;
        this.password = password;
        this.realName = realName;
        this.isEnable = isEnable;
        this.isExpired = isExpired;
        this.isLock = isLock;
        this.isCredentials = isCredentials;
        this.grantedAuthorities = grantedAuthorities;
        this.createTime = createTime;
        this.loginTime = loginTime;
    }

    //角色的集合
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return grantedAuthorities;
    }

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

    //使用者名稱
    @Override
    public String getUsername() {
        return username;
    }

    //賬號是否存在
    @Override
    public boolean isAccountNonExpired() {
        return isExpired;
    }

    //賬號是否鎖定
    @Override
    public boolean isAccountNonLocked() {
        return isLock;
    }

    //是否過期
    @Override
    public boolean isCredentialsNonExpired() {
        return isCredentials;
    }

    //是否啟用
    @Override
    public boolean isEnabled() {
        return isEnable;
    }



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

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

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

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public void setEnable(boolean enable) {
        isEnable = enable;
    }

    public void setExpired(boolean expired) {
        isExpired = expired;
    }

    public void setLock(boolean lock) {
        isLock = lock;
    }

    public void setCredentials(boolean credentials) {
        isCredentials = credentials;
    }

    public void setGrantedAuthorities(List<GrantedAuthority> grantedAuthorities) {
        this.grantedAuthorities = grantedAuthorities;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public void setLoginTime(Date loginTime) {
        this.loginTime = loginTime;
    }

    public Integer getId() {
        return id;
    }

    public String getRealName() {
        return realName;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public Date getLoginTime() {
        return loginTime;
    }

    @Override
    public String toString() {
        return "SysUser{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", realName='" + realName + '\'' +
                ", isEnable=" + isEnable +
                ", isExpired=" + isExpired +
                ", isLock=" + isLock +
                ", isCredentials=" + isCredentials +
                ", grantedAuthorities=" + grantedAuthorities +
                ", createTime=" + createTime +
                ", loginTime=" + loginTime +
                '}';
    }
}
public class SysRole {

    private Integer id;
    private String role;
    private String rolememo;

    public Integer getId() {
        return id;
    }

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

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getRolememo() {
        return rolememo;
    }

    public void setRolememo(String rolememo) {
        this.rolememo = rolememo;
    }

    @Override
    public String toString() {
        return "SysRole{" +
                "id=" + id +
                ", role='" + role + '\'' +
                ", rolememo='" + rolememo + '\'' +
                '}';
    }
}
2.4.2dao層和對應的xml檔案
@Repository
public interface SysRoleMapper {

    //根據使用者id檢視角色
    List<SysRole> selectByUserId(Integer userId);
}


@Repository
public interface SysUserMapper {

    //插入使用者
    int insertSysUser(SysUser sysUser);

    //根據使用者名稱查詢使用者
    SysUser selectByUsername(String username);
}

<?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="com.huang.security.mapper.SysRoleMapper">

    <resultMap id="roleMapper" type="com.huang.security.entity.SysRole">
        <id column="id" property="id"/>
        <result column="rolename" property="role"/>
        <result column="rolememo" property="rolememo"/>
    </resultMap>

    <select id="selectByUserId" resultMap="roleMapper" >
        SELECT sr.id,sr.rolename,sr.rolememo FROM sys_role AS sr
        INNER JOIN sys_user_role AS sur ON sr.id = sur.roleid
        WHERE sur.userid = #{userid}
    </select>
</mapper>
<?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="com.huang.security.mapper.SysUserMapper">

    <resultMap id="userMapper" type="com.huang.security.entity.SysUser">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realName"/>
        <result column="isenable" property="isEnable"/>
        <result column="islock" property="isLock"/>
        <result column="isexpire" property="isExpired"/>
        <result column="incredentials" property="isCredentials"/>
        <result column="createtime" property="createTime"/>
        <result column="logintime" property="loginTime"/>

    </resultMap>

    <insert id="insertSysUser" parameterType="com.huang.security.entity.SysUser">
        insert into sys_user(username,password,realname,isenable,islock,incredentials,createtime,logintime)
         values(#{username},#{password},#{realName},#{isEnable},#{isLock},#{isCredentials},
                #{createTime},#{loginTime})
    </insert>

    <select id="selectByUsername" resultMap="userMapper" >
        select id,username,password,realname,isenable,islock,isexpire,incredentials,createtime,logintime
        from sys_user where username = #{username}
    </select>
</mapper>
2.5service層

service層要實現UserDetailService介面,去獲取資料庫中的資訊做返回

@Service
public class UserWnoRoleService implements UserDetailsService {

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysRoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根據使用者名稱獲取使用者
        SysUser user = userMapper.selectByUsername(username);

        System.out.println("==== Service =====");
        String roleName = "";
        List<GrantedAuthority> list = new ArrayList<>();

            System.out.println("User" + user);
        if (!StringUtils.isEmpty(user)) {
            //根據使用者id獲取對應角色
            List<SysRole> roles = roleMapper.selectByUserId(user.getId());

            for (SysRole role : roles) {
                //一個使用者可能有多個角色,用集合儲存,放到使用者的集合裡
                roleName = role.getRole();
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + roleName);
                list.add(authority);
                user.setGrantedAuthorities(list);
            }

            //返回的這個user是包含角色的
            return user;
        }
        //可以返回自定義user,是因為實體類實現了UserDetails這個介面
        return user;
    }
}
2.6配置檔案
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/庫名?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=使用者名稱
spring.datasource.password=密碼

mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
# 包起別名
mybatis.type-aliases-package=com.huang.security.entity
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2.7相關的配置類
@Configuration
//@EnableWebSecurity //如果是匯入的jar包是spring-boot-starter-security可以不用寫
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("======  MySecurityConfig   configure==============");

        //匹配"/index","/login.html","/login"  不用驗證(permit 許可),和登入相關的要放行
        http.authorizeRequests().antMatchers("/index","/login.html","/login").permitAll()
                //匹配只有相關角色才能訪問的路徑
                .antMatchers("/access/user/**").hasRole("USER")
                .antMatchers("/access/read/**").hasRole("READ")
                .antMatchers("/access/admin/**").hasRole("ADMIN")
                //所有都需要驗證
                .anyRequest().authenticated()
                //執行結束
                .and()
                //表單的方式登入
                .formLogin()
                //登入的自定義檢視頁面
                .loginPage("/login.html")
                //登入訪問的地址,表單中action的值
                .loginProcessingUrl("/login")
                .and()
                //跨域安全的設定,禁用
                .csrf().disable();
    }

    @Qualifier("userWnoRoleService")
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        // userDetailsService使用的是service層的 UserWnoRoleService,它實現了 UserDetailsService
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }
}
2.8html頁面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    身份驗證 <br>
    <a href="/access/user">zs</a> <br>
    <a href="/access/read">lisi</a> <br>
    <a href="/access/admin">admin</a> <br>
    <a href="/logout">退出</a>
</body>
</html>






<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>自定義登入頁</p>
    <form action="/login" method="post">
        使用者名稱:<input type="text" name="username" value=""><br/>
        密&nbsp;&nbsp;&nbsp;碼:<input type="password" name="password" value=""><br/>
        <input type="submit" value="登入">
    </form>
</body>
</html>
2.9controller層測試
@Controller
public class InitController {

    @GetMapping("index")
    public String toIndex() {
        return "forward:/index.html";
    }
}
@RestController
@RequestMapping("/access")
public class UserWnoRoleController {

    @GetMapping("user")
    public String sayUser() {
        return "zs 是 user 角色";
    }

    @GetMapping("read")
    public String sayRead() {
        return "lisi 是 read 角色";
    }

    @GetMapping("admin")
    public String sayAdmin() {
        return "admin 是 user admin 角色";
    }
}

個人筆記