博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javaweb——Spring Boot 系列(16)认证与授权
阅读量:3934 次
发布时间:2019-05-23

本文共 15704 字,大约阅读时间需要 52 分钟。

认证与授权

  • 为了系统具有一定的安全性,也为了将普通用户和管理员用户区分开来,大多数企业级的 Web 都配备了安全机制,用户认证与授权就是一种。

一、Spring Boot 对认证与授权机制的支持

  • 作为优秀的框架 Spring 或者说 Spring Boot 想当然的对用户认证与授权机制进行了支持,Spring 框架提供了一个 Spring Security 组件为 Spring 项目提供一个安全框架,利用依赖注入和 AOP 实现安全相关的功能。
  • 作为安全框架,认证(Authentication)和认证(Authorization)是整个框架中相当重要的概念,认证确定用户是否可以登录系统,授权确定用户可以使用哪些系统功能。

1、Spring Security 的如何配置

  • Spring Security 主要通过过滤器来过滤访问请求从而达到安全的功能,在不使用 Spring Boot 的情况下 Spring Security 的配置,需要注册一个特殊的 DelegatingFilterProxy 过滤器到 WebApplicationInitializer;或者在自己的 Initializer 类继承 AbstractSecurityWebApplicationInitializer 抽象类。
  • 通常在一个配置类上用注解 @EnableWebSecurity 并继承 WebSecurityConfigurerAdapter 便能达到对 Spring Security 的配置,具体的安全配置则通过重写 config 方法实现。
  • 而在 Spring Boot 中的话,已经为我们准备了对 Spring Security 的自动配置:SecurityAutoConfiguration 类和 SecurityProperties 类。前者主要有如下的自动配置:
    1)自动配置一个内存中的用户,账号为 user,密码在项目启动时在控制台窗口可以看到,相当复杂的一串字符;
    2)忽略 /css/、/js/、/iamges/ 和 /favicon.ico 等静态文件的拦截。
    3)自动配置 securityFilterChainRegistration 的 Bean。
  • 而 SecurityProperties 提供在 application.properties 文件中以 “security” 为前缀的相关配置,主要如下:
    securtiy.user.name=user # 内存中的默认用户账户securtiy.user.password= # 用户密码securtiy.user.role = USER # 用户角色,默认是 USERsecurtiy.require-ssl = false # 是否需要 SSL 支持,默认不需要securtiy.enable-csrf=false # 是否开启“跨站请求伪造” 支持。默认关闭securtiy.basic.enabled = truesecurtiy.basic.path= # /**securtiy.basic.authorize -mode=securtiy.filter-order=0securtiy.headers.xss=falsesecurtiy.headers.cache=falsesecurtiy.headers.frame=falsesecurtiy.headers.content-type=falsesecurity.headers.hsts=allsecurity.sessions=statelesssecurity.igonre= # 用逗号隔开无需拦截的路径
  • 因为 Spring Boot 为我们做了大量自动配置,因此只需要在配置类继承 WebSecurityConfigurerAdapter 类即可。

2、进行认证的用户

  • 认证需要有一套用户资源,授权就是对某个用户赋予相应的角色权限,Spring Security 通过重写 config(AuthenticationManagerBuilder auth) 方法实现定制。
  • 用户可以是内存中的用户,也可以是 JDBC 中的用户,前者用 AuthenticationManagerBuilder 的 inMemoryAuthentication 方法添加并指定用户权限即可,示例:
    @Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{
    auth.inMemoryAuthentication .withUser("ts").password("ts").roles("ROLE_ADMIN") .and() .withUser("demo").password("demo").roles("ROLE_USER");}
  • 而 对于 JDBC 中的用户,直接指定 DataSource 即可,如下:
    @AutowiredDataSource dataSource;@Overrideprotected void configure(AuthenicationManagerBuild auth) throws Exception{
    auth.jdbcAuthentication().dataSource(dataSource);}
  • 当然用户来源也可以是其他的,此时可以自定义实现 UserDetailsService 接口,因为上面的两种其实就是对该接口的一种实现,自定义示例如下:
    public class CustomUserService implements UserDetailsService{
    @Autowired customRepository repository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    customUser user = repository.findByUserName(username); List
    auth = new ArrayList<>(); auth.add(new SimpleGrantedAuthority("ROLE_ADMIN"); return new User(user.getUsername(),user.getPassword(),auth); }}
  • 然后再对这个 CustomUserService 进行注册,示例:
    @BeanUserDetailsService customUserService(){
    return new CustomUserService();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{
    auth.userDetailsService(customUserService());}

3、如何进行授权

  • Spring Security 通过拦截器对请求进行过滤和拦截,从而起到一种安全措施,只用被授权允许访问的用户才不会被拦截。
  • 实现请求拦截通过重写 configure(HttpSecurity http) 方法。Spring Security 有两种规则来对请求路径进行匹配:antMatchers 和 regesMatchers;前者用 Ant 风格的路径匹配,后者用正则表达式匹配路径。
  • 在路径匹配之后,是对请求访问的用户,针对其用户信息对请求路径进行安全处理,相关方法如下:
    方法 作用
    access(String) Spring EL 表达式结果为 true 时可访问
    anonymous() 匿名可访问
    denyAll() 用户不能访问
    fullyAuthenticated() 用户完全认证可访问(非 remember 下自动登录)
    hasAnyAuthority(String…) 如果用户有参数,则其中任一权限可访问
    hasAnyRole(String…) 如果用户有参数,则其中任一角色可访问
    hasAuthority(String) 如果用户有参数,则其权限可访问
    hasIpAddress(String) 如果用户来自参数中的 IP 可访问
    hasRole(String) 用户若有参数中的角色可以访问
    permitAll() 用户可以任意访问
    rememberMe() 允许通过 remember-me 登录的用户访问
    authenticated() 用户登录后可访问
  • 授权示例:
    @Overrideprotected void configure(HttpSecurity http) throws Exception{
    http.authorizeRequests() // 开始请求权限配置 .antMatchers("/admin/**").hasRole("ROLE_ADMIN) //只有拥有 ROLE_ADMIN 角色的用户才可以访问路径 /admin/** .antMatchers("/user/**").hasAnyRole("ROLE_ADMIN","ROLE_USER") .anyRequest().authenticated(); //其余所有用户需要登录认证才能访问}

4、登陆行为定制

  • Spring Security 支持对登录行为进行定制,如下:
    @Overrideprotected void configure(HttpSecurity http) throws Exception{
    http .formLogin() //开始登录制作 .loginPage("/login") //登录页面 .defaultSuccessUrl("/index") //登录成功后跳转的页面 .failureUrl("/login?error") //登录失败后跳转的页面 .permitAll() .and() .rememberMe() //开启 Cookie 存储用户信息 .tokenVailditySeconds(1209600) //指定 cookie 的有效期为1209600秒,即两个星期 .key("myKey") // cookie 中的私钥 .and() .logout() //定制注销行为 .logoutUrl("/custom-logout") //指定注销的 URL 路径 .logoutSuccessUrl("/logout-success") //注销成功后跳转页面 .permitAll();}

三、完整项目示例

  • 接下来用一个完整的项目演示登录与注销行为,以及对不同登录用户进行认证和授权行为。

1、新建项目

  • 新建一个 Spring Boot 项目,因为演示页面的原因需要 Thymeleaf 的支持,因此初始依赖选择 JPA、Security 和 Thymeleaf,目标数据库为 Oracle,因此需要 ojdbc6.jar 这个驱动包,同时为了 Thymeleaf 页面支持 Spring Security 需要添加依赖,因此需要手动添加如下两个依赖:
    com.oracle
    ojdbc6
    11.2.0.2.0
    org.thymeleaf.extras
    thymeleaf-extras-springsecurity4

2、项目配置

  • 在 application.properties 对数据源和日志等进行配置,如下:
    spring.datasource.driver-class-name=oracle.jdbc.OracleDriverspring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xespring.datasource.username=bootspring.datasource.password=bootlogging.file=log.loglogging.level.org.springframework.security=INFOspring.thymeleaf.cache=falsespring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=true

3、静态资源

  • 为了页面稍微美观,又不想自己写那么多 CSS,所以直接用先成的前端框架 Bootstrap,为了避免静态资源被错误拦截,因此放在指定位置,CSS 文件默认放置在 src/main/resources/static/css 目录中,因此 bootstrap.min.css 文件就放在这个目录,由于文件代码量多,并且是可以通过官网下载的,因此这里不贴出具体代码。

4、用户和角色

  • 用 JPA 创建用户和角色。

4.1、角色定义

  • 因为用户定义时,需要用到角色,因此先把角色类定义了,我习惯于先把被引用的类先创建和编辑。具体代码如下:
    package com.pyc.mysecurity.domain;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;@Entitypublic class SysRole {
    @Id @GeneratedValue private Long id; private String name; public void setId(Long id) {
    this.id = id; } public Long getId() {
    return id; } public void setName(String name) {
    this.name = name; } public String getName() {
    return name; }}
  • 项目运行后,JPA 自动在后台数据库创建对应的数据表。

4.2、用户定义

  • 创建另一个实体类,用于定义用户,代码如下:
    package com.pyc.mysecurity.domain;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import javax.persistence.*;import java.util.ArrayList;import java.util.Collection;import java.util.List;// 令用户实体实现 UserDetails 接口,从而用户实体即为 Spring Security 所使用的用户// Make the user entity implement the UserDetails interface so that// the user entity is the user used by Spring Security@Entitypublic class SysUser implements UserDetails {
    private static final Long serialVersionUID=1L; @Id @GeneratedValue private Long id; private String username; private String password; // 配置用户和角色的多对多关系 // Configure many-to-many relationships for users and roles @ManyToMany(cascade = {
    CascadeType.REFRESH},fetch = FetchType.EAGER) private List
    roles; // 重写 getAuthorities 方法,将用户的角色作为权限 // Overwrite the getAuthorities method so that can make the roles of user became authority @Override public Collection
    getAuthorities(){
    List
    authorities = new ArrayList
    (); List
    roles = this.getRoles(); for(SysRole role:roles){
    authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } @Override public boolean isAccountNonExpired() {
    return true; } @Override public boolean isAccountNonLocked() {
    return true; } @Override public boolean isCredentialsNonExpired() {
    return true; } @Override public boolean isEnabled() {
    return true; } public void setId(Long id) {
    this.id = id; } public Long getId() {
    return id; } public void setPassword(String password) {
    this.password = password; } @Override public String getPassword() {
    return password; } public void setUsername(String username) {
    this.username = username; } @Override public String getUsername() {
    return username; } public List
    getRoles() {
    return roles; } public void setRoles(List
    roles) { this.roles = roles; }}

5、数据表初始数据

  • 在 resources 目录下新建一个 data.sql,编辑如下:
    insert into SYS_USER(id, username, password)values (1, 'pyc', 'pyc');insert into SYS_USER(id, username, password)values (2, 'ycy', 'ycy');insert into SYS_ROLE(id, name) values (1, 'ROLE_ADMIN');insert into SYS_ROLE(id, name) values (2, 'ROLE_USER');insert into SYS_USER_ROLES(SYS_USER_ID, ROLES_ID) values (1, 1);insert into SYS_USER_ROLES(SYS_USER_ID, ROLES_ID) values (2,2);
  • 在第一次运行后,记得删除或改名。

6、传值对象

  • 测试不同角色的用户的数据展示,代码如下:
    package com.pyc.mysecurity.domain;public class Msg {
    private String title; private String content; private String etraInfo; public Msg(String title, String content, String etraInfo){
    super(); this.content=content; this.title=title; this.etraInfo=etraInfo; } public void setTitle(String title) {
    this.title = title; } public String getTitle() {
    return title; } public void setContent(String content) {
    this.content = content; } public String getContent() {
    return content; } public void setEtraInfo(String etraInfo) {
    this.etraInfo = etraInfo; } public String getEtraInfo() {
    return etraInfo; }}

7、Repository

  • 因为用的是 JPA,因此需要实体类的 Repository,这里只需编辑一个按名称查找的方法,代码如下:
    package com.pyc.mysecurity.dao;import com.pyc.mysecurity.domain.SysUser;import org.springframework.data.jpa.repository.JpaRepository;public interface SysUserRepository extends JpaRepository
    {
    SysUser findByUsername(String username);}

8、自定义 UserDetailsService

  • 为了符合要求,自定义一个 UserDetailsService,代码如下:
    package com.pyc.mysecurity.service;import com.pyc.mysecurity.dao.SysUserRepository;import com.pyc.mysecurity.domain.SysUser;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;// 自定义需实现 UserDetailsService 接口// Custom service needs to implement UserDetailsService interface@Servicepublic class CustomUserService implements UserDetailsService {
    @Autowired SysUserRepository userRepository; // overwrite loadUserByUsername method to get account @Override public UserDetails loadUserByUsername(String username){
    SysUser user = userRepository.findByUsername(username); if(user == null){
    throw new UsernameNotFoundException("用户名不存在"); } return user; }}

9、Config

  • Config 类有 Spring MVC 的和 Spring Security 的。

9.1、WebMvcConfig

  • 对 Spring MVC 进行配置
    package com.pyc.mysecurity.config;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/login").setViewName("login"); }}

9.2、WebSecurityConfig

  • 对 Spring Security 进行配置,诸如登陆行为、用户授权和认证
    package com.pyc.mysecurity.config;import com.pyc.mysecurity.service.CustomUserService;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.core.userdetails.UserDetailsService;// 拓展的 Spring Security 配置需要继承 WebSecurityConfigurerAdapter// extend spring security need to extend WebSecurityConfigurerAdapter@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    // booking a bean of CustomUserService @Bean UserDetailsService customUserService(){
    return new CustomUserService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 添加自定义的 user detail service 认证 // add custom user detail service authentication auth.userDetailsService(customUserService()); } @Override protected void configure(HttpSecurity http) throws Exception {
    // any request must to authorize so that can login http.authorizeRequests().anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error") .permitAll() .and() .logout().permitAll(); }}

10、视图页面

  • 这里的视图页面主要有两个,一个登录页面和一个登录成功后展示的页面。

10.1、Login Page

  • 登陆页面用 Thymeleaf 编辑,如下:
        
    登录

    Successfully logout

    Happening some error, please try again

    Login by account and password

  • 页面内容比较简单,一个导航栏和一个登陆框。

10.2、Home Page

  • 该页面为登录成功后出现的页面,仍然用 Thymeleaf 编辑,如下:

    Not too much message to display

  • 页面由一个导航栏和简单的页面内容构成,页面内容根据用户角色的不同而展示不同信息。

11、controller

  • 最后编辑一个 Controller,代码如下:
    package com.pyc.mysecurity.web;import com.pyc.mysecurity.domain.Msg;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controllerpublic class WebController {
    @RequestMapping("/") public String index(Model model){
    Msg msg = new Msg("Demo Title", "Demo Content", "additional msg, only admin can see"); model.addAttribute("msg",msg); return "home"; }}
  • 入口类无需修改和编辑。

四、项目测试

  • 运行项目测试功能是否正常。初始页面如下:
    在这里插入图片描述

1、成功登录

  • 先测试成功登录的情况。

1.1、管理员登录

  • 测试以管理员身份登录,主页页面显示内容是否正常。登录成功后页面如下:
    在这里插入图片描述
  • 标签页标签显示信息:
    在这里插入图片描述

1.2、用户登录

  • 用用户角色的用户登录,页面如下:
    在这里插入图片描述
  • 标签页标签显示信息:
    在这里插入图片描述
  • 主页显示情况正常,管理员和普通用户显示不同的信息。

2、注销测试

  • 无论是管理员用户登录,还是普通用户登录,注销后页面如下:
    在这里插入图片描述
  • 显示正常。

3、错误登录测试

  • 用不正确的用户密码登录
    在这里插入图片描述
  • 显示正常。

4、程序控制台

  • 在前台进行登录、注销等行为时,控制台输出关键 SQL 语句信息:
    在这里插入图片描述

5、查看后台数据库

  • 用管理工具打开后台数据库,可以发现数据库多了三个数据表:
    在这里插入图片描述
  • 三个表的内容:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 可以总结,项目运行正常。

转载地址:http://flqgn.baihongyu.com/

你可能感兴趣的文章
read tetgen file
查看>>
sub figure without letter numbering
查看>>
my plot mesh
查看>>
文章索引加1
查看>>
Laplacian surface editing
查看>>
求cluster的质心坐标
查看>>
Deformable 3D shape registration based on local similarity transforms
查看>>
intersectLineMesh3d
查看>>
how change files in matlab2 , cell 函数的运用
查看>>
sort_nat
查看>>
how to fix the frame size?
查看>>
write frames to a video
查看>>
export frames as pictures
查看>>
AE的一些基本知识
查看>>
perform_farthest_point_sampling_mesh
查看>>
perform_faces_reorientation
查看>>
compute_voronoi_triangulation_mesh
查看>>
differential forms
查看>>
QTCreator中字符编码问题以及WINDOWS API A/W两种版本的说明
查看>>
变量命名
查看>>