栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Security 入门篇02

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Security 入门篇02

功能

查询数据库,获取用户信息,赋予用户角色交给Security管理
自定义登录页面、无权访问跳转页面
跨域

1 github: 源码地址 2 security02 子工程


    4.0.0

    
        com.yzm
        security
        0.0.1-SNAPSHOT
        ../pom.xml 
    

    security02
    0.0.1-SNAPSHOT
    security02
    jar
    Demo project for Spring Boot

    
        
            com.yzm
            common
            0.0.1-SNAPSHOT
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



项目结构

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test04?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:/mapper
@Service
public class SecUserDetailsServiceImpl implements UserDetailsService {

    private final UserService userService;
    private final RoleService roleService;

    public SecUserDetailsServiceImpl(UserService userService, RoleService roleService) {
        this.userService = userService;
        this.roleService = roleService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            throw new UsernameNotFoundException(String.format("用户'%s'不存在", username));
        }

        List roleIds = Arrays.stream(user.getRIds().split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());
        List roleList = roleService.listByIds(roleIds);
        List authorities = roleList.stream()
                .map(Role::getRName)
                .distinct()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        // 这里我们的权限只是基于角色的
        return new org.springframework.security.core.userdetails.User(username, user.getPassword(), authorities);
    }
}

loadUserByUsername 返回的 UserDetails 对象封装了用户信息,但它是一个接口
Security给我们默认提供了org.springframework.security.core.userdetails.User,所以使用new User()返回

5 SecurityConfig

把自定义的UserDetailsService交给Security管理,以便Security查询数据
自定义了登录页面和无权的响应页面
设置url拦截管理

package com.yzm.security02.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Slf4j
@Configuration
@EnableWebSecurity // 开启Security服务
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启全局注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(@Qualifier("secUserDetailsServiceImpl") UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 从数据库读取用户、并使用密码编码器解密
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    //配置资源权限规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 关闭CSRF跨域
                .csrf().disable()

                // 登录
                .formLogin()
                .loginPage("/auth/login") //指定登录页的路径,默认/login
                .loginProcessingUrl("/login") //指定自定义form表单请求的路径(必须跟login.html中的form action=“url”一致)
                .defaultSuccessUrl("/home", true) // 登录成功后的跳转url地址
                .failureUrl("/auth/login?error") // 登录失败后的跳转url地址
                .permitAll()
                .and()

                .exceptionHandling()
                .accessDeniedPage("/401") // 拒接访问跳转页面
                .and()

                // 退出登录
                .logout().permitAll()
                .and()

                // 访问路径URL的授权策略,如注册、登录免登录认证等
                .authorizeRequests()
                .antMatchers("/", "/home", "/register", "/auth/login").permitAll() //指定url放行
                .antMatchers("/user
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/auth/login").setViewName("login");
        registry.addViewController("/401").setViewName("401");
    }

    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")    // 允许跨域访问的路径
                .allowedOriginPatterns("*")    // 允许跨域访问的源
                .allowedMethods("*")    // 允许请求方法
                .allowedHeaders("*")  // 允许头部设置
                .maxAge(168000)    // 预检间隔时间
                .allowCredentials(true);    // 是否发送cookie
    }
}
接口
package com.yzm.security02.controller;


import com.alibaba.fastjson.JSONObject;
import com.yzm.common.entity.HttpResult;
import com.yzm.security02.entity.User;
import com.yzm.security02.service.UserService;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController {

    private final UserService userService;
    private final PasswordEncoder passwordEncoder;

    public HomeController(UserService userService, PasswordEncoder passwordEncoder) {
        this.userService = userService;
        this.passwordEncoder = passwordEncoder;
    }

    @PostMapping("register")
    @ResponseBody
    public Object register(@RequestParam String username, @RequestParam String password) {
        User user = new User();
        user.setUsername(username);
        // 密码加密
        user.setPassword(passwordEncoder.encode(password));
        userService.save(user);
        return HttpResult.ok("注册成功");
    }

    @GetMapping("/info")
    @ResponseBody
    public void info(HttpServletResponse response, @AuthenticationPrincipal UserDetails userDetails) throws IOException {
        HttpUtils.successWrite(response, userDetails);
    }
}

package com.yzm.security02.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {

    @GetMapping("/select")
    public Object select() {
        return "Select";
    }

    @GetMapping("/create")
    public Object create() {
        return "Create";
    }

    @GetMapping("/update")
    public Object update() {
        return "Update";
    }

    @GetMapping("/delete")
    public Object delete() {
        return "Delete";
    }

}

package com.yzm.security02.controller;


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/select")
    public Object select() {
        return "Select";
    }

    @GetMapping("/create")
    public Object create() {
        return "Create";
    }

    @GetMapping("/update")
    public Object update() {
        return "Update";
    }

    @GetMapping("/delete")
    public Object delete() {
        return "Delete";
    }

}

页面

home.html




    
    首页


注册

用户详情

user

User角色,拥有 select 权限

User角色,拥有 create 权限

User角色,拥有 update 权限

User角色,拥有 delete 权限

admin

Admin角色,拥有 select 权限

Admin角色,拥有 create 权限

Admin角色,拥有 update 权限

Admin角色,拥有 delete 权限

login.html




    
    登录页


You have been logged out.

You username or password is wrong

用户名密码登录

Remember me on this computer.

401.html




    
    401


抱歉,你无权访问

7 测试

启动项目,注册admin、yzm

修改数据库,使用yzm的角色是2,admin是1,2双重身份,角色和权限就手动新增了
具体可以参考 上面的3 用户数据

数据修改完成后,返回主页,点击用户详情,跳转到我们自定义的登录页,默认是/login

.loginPage("/auth/login") //指定登录页的路径,默认/login


登录yzm,登录失败跳转

.failureUrl("/auth/login?error") // 登录失败后的跳转url地址


登录成功跳转

.defaultSuccessUrl("/home", true) // 登录成功后的跳转url地址


我们给yzm赋予的角色是USER

而设置url的也是USER

.authorizeRequests()
    .antMatchers("/", "/home", "/register", "/auth/login").permitAll() //指定url放行
    .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 需要角色
    .antMatchers("/admin/**").hasRole("ADMIN") // 需要角色
    .anyRequest().authenticated() //其他任何请求都需要身份认证

但我们访问不了这些接口


这是因为Security自动给我们加了前缀ROEL_

.antMatchers("/user/**").hasAnyRole("ADMIN", "USER") // 需要角色

 private static String hasAnyRole(String... authorities) {
     String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
     return "hasAnyRole('ROLE_" + anyAuthorities + "')";
 }

修改数据库,如下

重启后就可以正常访问了 /user/** 接口
访问 /admin/** 还是无权,跳转到401

.exceptionHandling()
.accessDeniedPage("/401") // 拒接访问跳转页面
.and()
hasRole(hasAnyRole) 和 hasAuthority(hasAnyAuthority) 的 区别

hasRole:会自动添加前缀,hasRole(“ADMIN”) --> ROLE_ADMIN,即数据得存ROLE_ADMIN
hasAuthority:不会添加前缀,hasAuthority(“ADMIN”) 则数据库得存ADMIN

.antMatchers("/user/**").hasAnyRole("ADMIN", "USER") 
.antMatchers("/admin/**").hasRole("ADMIN") 
// 把hasRole换成hasAuthority
.antMatchers("/user/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER") 
.antMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/345167.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号