Spring Security

Spring Security

江南一点雨牛逼

添加security依赖,不需要配置,默认为所有接口进行安全管理

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.java</groupId>
    <artifactId>security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>security</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.5.3</version>
        </dependency>
    </dependencies>

<!--    <build>-->
<!--        <plugins>-->
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
<!--        </plugins>-->
<!--    </build>-->

</project>

密码在项目启动时随机生成

用户名user

配置用户名和密码
server.port=8888
spring.security.user.name=java
spring.security.user.password=123
spring.security.user.roles=admin

或者使用配置文件

spring5开始之后,security中的密码必须加密

package org.java.config;

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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //实例表示用户不需要加密,可以直接登录,方案已经过期
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存认证
        auth.inMemoryAuthentication()
                .withUser("java").password("123").roles("user")
                .and()
                .withUser("admin").password("123").roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //匹配用户
        http.authorizeRequests()
//                匹配路径
                .antMatchers("/admin/**").hasRole("admin")
                //具备多个角色中的一个
                .antMatchers("user/**").hasAnyRole("admin","user")
//                其他路径需要认证之后访问
                .anyRequest().authenticated()
                .and()
                //表单登录
                .formLogin()
                //表单登录的路径,默认为login,postman测试最好配置一下
                .loginProcessingUrl("/doLogin")
                //和登录相关的接口可以直接访问
                .permitAll()
                .and()
                //csrf攻击,测试时取消
                .csrf().disable();

    }
}

//具备多个角色中的一个
                .antMatchers("user/**").access("hasAnyRole('user','admin')")
.loginProcessingUrl("/doLogin")
                //登录页面,访问接口需要登录的话会跳转到登录页面上去,可以自己进行配置
.loginPage("/login")
登录表单的详细配置

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //实例表示用户不需要加密,可以直接登录,方案已经过期
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存认证
        auth.inMemoryAuthentication()
                .withUser("java").password("123").roles("user")
                .and()
                .withUser("admin").password("123").roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //匹配用户
        http.authorizeRequests()
//                匹配路径
                .antMatchers("/admin/**").hasRole("admin")
                //具备多个角色中的一个
                .antMatchers("user/**").access("hasAnyRole('user','admin')")
//                其他路径需要认证之后访问
                .anyRequest().authenticated()
                .and()
                //表单登录
                .formLogin()
                //表单登录的路径,默认为login,postman测试最好配置一下
                .loginProcessingUrl("/doLogin")
                //登录页面,访问接口需要登录的话会跳转到登录页面上去,可以自己进行配置
                .loginPage("/login")
                //配置用户名和密码参数
                .usernameParameter("uname")
                .passwordParameter("passwd")
                //登录后服务端跳转接口.前后端不分离
//                .successForwardUrl("")
                //前后端分离,提供json数据,登录成功的处理
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        //json数据
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",200);
                        //获取登录对象
                        map.put("msg",authentication.getPrincipal());
                        //输出为json字符串
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();

                    }
                })
                //登录失败的方式
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
                        //json数据
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",401);
                        if(e instanceof LockedException){
                        map.put("msg","账户被锁定,登录失败");
                        }else if(e instanceof BadCredentialsException){
                            map.put("msg","用户名或密码输入错误,登录失败");
                        }else if(e instanceof DisabledException){
                            map.put("msg","账户被禁用,登录失败");
                        }else if(e instanceof AccountExpiredException){
                            map.put("msg","账户过期,登录失败");
                        }else if(e instanceof CredentialsExpiredException){
                            map.put("msg","密码过期,登录失败");
                        }else{
                            map.put("msg","登录失败");
                        }

                        //输出为json字符串
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                //和登录相关的接口可以直接访问
                .permitAll()
                .and()
                //csrf攻击,测试时取消
                .csrf().disable();

    }
}

ctrl+H查看继承关系

"authorities": [
            {
                "authority": "ROLE_admin"
            }
        ],

authority会自动加上ROLE_前缀

注销登录
//和登录相关的接口可以直接访问
                .permitAll()
                .and()
                .logout()
                //注销登录
                .logoutUrl("/logout")
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
                        //json数据
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String,Object> map = new HashMap<>();
                        map.put("status",200);
                        //获取登录对象
                        map.put("msg","注销登录成功");
                        //输出为json字符串
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                //csrf攻击,测试时取消
                .csrf().disable();
多个HttpSecurity
package org.java.config;

@Configuration
public class MultiHttpSecurityConfig {

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }


//    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存认证
        auth.inMemoryAuthentication()
                .withUser("java").password("123").roles("user")
                .and()
                .withUser("admin").password("123").roles("admin");
    }
    @Configuration
    //设置优先级
    @Order(1)
    //先和这个函数匹配,如果匹配上面,再和下面的函数进行匹配
    public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasAnyRole("admin");
        }
    }

    @Configuration
    public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginProcessingUrl("/doLogin")
                    .permitAll()
                    .and() 
                    .csrf().disable();
        }
    }
}

密码加密

可逆加密,对称加密,非对称加密

密码加盐:相同明文加密后生成的字段不相同,维护盐字段


@RunWith(SpringRunner.class)
@SpringBootTest
public class SecurityApplicationTests {
    @Test
    public void contextLoads(){
        for (int i = 0; i < 10; i++) {
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            System.out.println(encoder.encode("123"));

        }
    }

}

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


//    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存认证
        auth.inMemoryAuthentication()
                .withUser("java").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("user")
                .and()
                .withUser("admin").password("$2a$10$wBu9WqZVu7IrVd.9E1P1EuvpHfkEqUsTjpXs3UkwxUelMKFWq4ppi").roles("admin");
    }
方法安全

加一个注解

@Configuration
//方法执行之前进行校验,securedEnabled可以写表达式
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MultiHttpSecurityConfig {

@Service
public class MethodService {

    @PreAuthorize("hasRole('admin')")
    public String admin(){
        return "hello admin";
    }

    @Secured("ROLE_user")
    public String user(){
        return "hello user";
    }

    @PreAuthorize("hasAnyRole('admin','user')")
    public String hello(){
        return "hello ha";
    }

}

    @Autowired
    MethodService methodService;
    @GetMapping("/hello1")
    public String hello1(){
        return methodService.admin();
    }
    @GetMapping("/hello2")
    public String hello2(){
        return methodService.user();
    }
    @GetMapping("/hello3")
    public String hello3(){
        return methodService.hello();
    }

基于数据库的认证

导入表,user,role,user_role

添加数据库依赖


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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
            <scope>runtime</scope>
        </dependency>
<!--        数据库驱动-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

配置文件

server.port=8888
#spring.security.user.name=java
#spring.security.user.password=123
#spring.security.user.roles=admin

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3307/javagirl?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

创建Role文件

package org.java.bean;

public class Role {
    private Integer id;
    private String usename;
    private String password;
    private Boolean enabled;
    private Boolean locked;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", usename='" + usename + '\'' +
                ", password='" + password + '\'' +
                ", enabled=" + enabled +
                ", locked=" + locked +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsename() {
        return usename;
    }

    public void setUsename(String usename) {
        this.usename = usename;
    }

    public String getPassword() {
        return password;
    }

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

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public Boolean getLocked() {
        return locked;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }
}

package org.java.bean;

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

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

//当系统需要调用属性去验证用户是否登录成功,每个人定义的属性不一样,这个函数可以获取用户名,密码
public class User implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> roles;

    public Integer getId() {
        return id;
    }

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

    @Override
    public String getUsername() {
        return username;
    }

    //账户是否未过期
    @Override
    public boolean isAccountNonExpired() {
        //没有过期
        return true;
    }

    //账户是否未锁定
    @Override
    public boolean isAccountNonLocked() {
        //账户未锁定
        return !locked;
    }

    //凭证是否未过期
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    //账户是否可用
    @Override
    public boolean isEnabled() {
        return enabled;
    }

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

    //返回用户的所有角色
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (Role role : roles) {
            authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getUsername()));
        }
        return authorities;
    }

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

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

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }
//      删掉,不然重复
//    public Boolean getLocked() {
//        return locked;
//    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

userService



@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;


    //根据用户名查询用户,参数为登录的用户名,返回用户对象
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if(user == null){
            throw new UsernameNotFoundException("用户不存在");
        }
        user.setRoles(userMapper.getUserRolesById(user.getId()));
        return user;
    }
}

userMapper接口

public interface UserMapper {
    User loadUserByUsername(String username);
    List<Role> getUserRolesById(Integer id);
}

userMapper.xml

<?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="org.java.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="org.java.bean.User">
        select * from user where username=#{username};
    </select>
    <select id="getUserRolesById" resultType="org.java.bean.Role" parameterType="java.lang.Integer">
        select * from role where id in (select rid from user_role where uid=#{id});
    </select>
</mapper>

java: 不兼容的类型: org.springframework.security.core.userdetails.User无法转换为org.java.bean.User

从数据库加载用户,用户具备相应的角色

角色继承

'>'关系

放在security配置中,SecurityConfig

@Bean
    RoleHierarchy roleHierarchy(){
        RoleHierarchy roleHierarchy = new RoleHierarchyImpl();
        String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
        roleHierarchy.setHierachy(hierarchy);
        return roleHierarchy;

    }
动态配置权限

menu

package org.java.bean;

import java.util.List;

public class Menu {
    private Integer id;
    private String pattern;
    private List<Role> roles;

    public Integer getId() {
        return id;
    }

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

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
}

menuService


@Service
public class MenuService {
    @Autowired
    MenuMapper menuMapper;
    public List<Menu> getAllMenus(){
        return menuMapper.getAllMenus();
    }
}

menuMapper


public interface MenuMapper {
    List<Menu> getAllMenus();
}

xml

<?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="org.java.mapper.MenuMapper">
    <resultMap id="BaseResultMap" type="org.java.bean.Menu">
        <id property="pattern" column="id"/>
        <result property="pattern" column="pattern"/>
        <collection property="roles" ofType="org.java.bean.Role">
            <id column="rid" property="id"/>
            <result column="rname" property="name"/>
            <result column="rnameZh" property="nameZh"></result>
        </collection>
    </resultMap>
    <select id="getAllMenus" resultMap="BaseResultMap">
        select m.*,r.`id` as rid,r.`name` as rname,r.`nameZh` as rnameZh from menu m left join menu_role mr on m.`id` =mr.`mid` left join role r on mr.`rid`=r.`id`;
    </select>
</mapper>

Config下的MyFilter文件

package org.java.config;

import org.java.bean.Menu;
import org.java.bean.Role;
import org.java.service.MenuService;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

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

//每次请求过滤,过滤器,分析出请求地址,确定地址需要什么角色
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
    //路径匹配符,可以直接匹配规则和路径
    AntPathMatcher pathMatcher = new AntPathMatcher();
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        //请求的地址
     String requestUrl =((FilterInvocation) o).getRequestUrl();
     //所有的菜单,pattern里面定义的规则
        List<Menu> allMenus = MenuService.getAllMenus();
        //遍历menu
        for (Menu menu : allMenus) {
            //匹配menu和请求的地址
            if(pathMatcher.match(menu.getPattern(),requestUrl)){
                //需要的角色
                List<Role> roles = menu.getRoles();
                //生成需要的角色列表
                String[] rolesStr = new String[roles.size()];
                for (int i = 0; i < roles.size(); i++) {
                    rolesStr[i] = roles.get(i).getName();

                }
                return SecurityConfig.createList(rolesStr);
            }
        }
        //额外的路径,等会儿做额外的处理
        return SecurityConfig.createList("ROLE_login");
        
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

MyAccessDecisionManager文件


public class MyAccessDecisionManager implements AccessDecisionManager {
    //第一个参数:登录成功后的参数 保存登录成功后用户信息,二哥参数:用来获取当前请求对象,第三个参数:MyFilter文件中的getAttribute的返回值(需要哪些角色)
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute attribute : collection) {
            //需要的角色和ROLE_login匹配,登录后就能访问,查看是否登录
            if("ROLE_login".equals(attribute.getAttribute())){
                if(authentication instanceof AnonymousAuthenticationToken){
                    throw new AccessDeniedException("非法请求,未登录");
                }else{
                    return;
                }
            }
            //其他的判断
            //需要的角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //如果需要ABC三个角色中的一个
            for (GrantedAuthority authority : authorities) {
                if(authority.getAuthority().equals(attribute.getAttribute())){
                    return;
                }
            }
            throw new AccessDeniedException("非法请求");
        }


    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}

SecurityConfig文件


    @Autowired
    MyFilter myFilter;
    @Autowired
    MyAccessDecisionManager myAccessDecisionManager;
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {

                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(MyAccessDecisionManager);
                        o.setSecurityMetadataSource(myFilter);
                        return o;
                    }
                })
                .and()
                .formLogin()
                .permitAll()
                .and()
                .csrf().disable;
    }

OAuth2协议

开放的标准,允许用户用第三方应用访问该用户在某个网站上存放的私密资源。QQ登录,微信登录,支付宝登录

登录时不需要用户名和密码

通过令牌实现,一定时间内的权限

角色:

  • 资源所有者,用户
  • 客户端
  • 授权服务器,验证用户提供的信息是否正确,返回令牌给第三方应用
  • 资源服务器,提供给用户资源的服务器

配置授权服务器,AuthorizationServerConfig

package org.java.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;


@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    //有四种认证模式。下面支持password认证模式,前后端分离登录。第三方登录使用授权码模式
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    RedisConnectionFactory redisConnectionFactory;
    @Autowired
    UserDetailsService userDetailsService;
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //配置在内存里面
        clients.inMemory()
                //认证模式为password
                .withClient("password")
                //配置授权模式,两种
                .authorizedGrantTypes("password","refresh_token")
                //token过期时间1800ms
                .accessTokenValiditySeconds(1800)
                //资源id
                .resourceIds("rid")
                .scopes("all")
                //密码,test文件中生成
                .secret("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //令牌的存储
        endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
    }
}

配置资源服务器ResourceServerConfig

package org.java.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //配置config方法,指定资源id
        resources.resourceId("rid").stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //资源服务器
        http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated();
    }
}

先在授权服务器获取授权,再在资源服务器上查找资源

配置SecurityConfig

package org.java.config;

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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected UserDetailsService userDetailsService() {
        return super.userDetailsService();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存认证
        auth.inMemoryAuthentication()
                .withUser("java").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("user")
                .and()
                .withUser("admin").password("$2a$10$uxLNq0J1Fm6cwlsAA3Z08u1Geh/ZLD85E18Wv8hC8SOjr4Jvp68K.").roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/oauth/**")
                .authorizeRequests()
                .antMatchers("/oauth/**").permitAll()
                .and().csrf().disable();

    }
}


controller接口

package org.java.controller;

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

@RestController
public class HelloController {
    @GetMapping("/admin/hello")
    public String admin(){
        return "hello admin";
    }
    @GetMapping("/user/hello")
    public String user(){
        return "hello user";
    }
    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }


}

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [org.java.OauthApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.class] cannot be opened because it does not exist

access_token:需要获取其它资源

refresh_token:刷新资源的token

expire_in:过期时间

SpringBoot整合Shiro

不想敲了,就听听吧

添加shiro-web依赖,shiro-spring依赖

配置shiro,extends AuthorizingRealm

ShiroConfig

使用官方的starter进行shiro整合

Spring Security使用JSON登陆

默认的登陆数据是key/value形式

连按两下shift搜索

源码

获取用户名UsernamePasswordAuthenticationFilter

@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}
		String username = obtainUsername(request);
		username = (username != null) ? username : "";
		username = username.trim();
		String password = obtainPassword(request);
		password = (password != null) ? password : "";
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
		// Allow subclasses to set the "details" property
		setDetails(request, authRequest);
		return this.getAuthenticationManager().authenticate(authRequest);
	}
protected String obtainUsername(HttpServletRequest request) {
		return request.getParameter(this.usernameParameter);
	}

配置MyAuthenticationFilter

package org.java.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            //说明用户以JSON形式传递参数
            //从request提取json数据
            String username = null;
            String password = null;
            try {
                Map<String,String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);
                username = map.get("username");
                password = map.get("password");
            } catch (IOException e) {
                e.printStackTrace();
            }
            username = (username != null) ? username : "";
            username = username.trim();
            password = (password != null) ? password : "";
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            // Allow subclasses to set the "details" property
            setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
        return super.attemptAuthentication(request, response);
    }
}

SecurityConfig

package org.java.Config;

import org.java.filter.MyAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin().permitAll()
                .and().csrf().disable();
        http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    MyAuthenticationFilter myAuthenticationFilter() throws Exception {
        MyAuthenticationFilter filter = new MyAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }
}

package org.java.controller;

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

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [org.java.SecurityJsonApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.class] cannot be opened because it does not exist

SpringSecurity整合JWT

无状态登陆oauth2或者jwt

添加依赖

<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值