美文网首页JAVA_Spring
Springboot 7 权限控制

Springboot 7 权限控制

作者: kason_zhang | 来源:发表于2018-11-03 12:25 被阅读1次

首先我们创建一个WebSecurityConfig类,继承自WebSecurityConfigurerAdapter, 然后编写基本类

package com.kason.config;

import org.springframework.context.annotation.Configuration;
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;

/**
 * Created by IBM on 2018/6/30.
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     *  Http权限控制
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/login").permitAll() //admin的登陆页面允许所有用户登入
        .antMatchers("/user/login").permitAll() //用户的登陆页面允许所有人登入
        .antMatchers("/static/**").permitAll() //静态资源允许全部获取权限
        .antMatchers("/admin/**").hasRole("ADMIN") //除了/admin/login之外的/admin/**只允许admin权限用户使用
        .antMatchers("/usr/**").hasAnyRole("ADMIN","USER")
                .antMatchers("/api/user/**").hasAnyRole("ADMIN","USER")
                .and()
                .formLogin()
                .loginProcessingUrl("/login") //配置角色登陆处理入口
        .and();
    }
}

这样当我们一开始输入http://localhost:8081/admin/center的时候就会跳转到http://localhost:8081/login登陆页面(此时是默认的SpringBoot的登陆页面),结果如图

默认登陆页面

自定义权限认证策略:

1 先使用内存配置进行测试

 /**
     * 自定义认证策略
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
        // 内存配置, 测试使用
        auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and();
    }

这样我们使用admin,admin来登陆http://localhost:8081/login 就可以登陆进行了。上面就是在内存中创建了一个ADMIN角色, 它的用户名是admin, 密码也是admin

2 使用数据库来完成角色认证

为了使用数据库实现自定义的权限认证,增加两张数据库表, user表和role表。 数据库数据信息如下:


user表
role表

针对这两张表编写对应的entity

package com.kason.entity;

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

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

@Entity
// 映射到数据库小写的表
@Table(name = "user")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String password;

    private String email;

    @Column(name = "phone_number")
    private String phoneNumber;

    private int status;

    @Column(name = "create_time")
    private Date createTime;

    @Column(name = "last_login_time")
    private Date lastLoginTime;

    @Column(name = "last_update_time")
    private Date lastUpdateTime;

    private String avatar;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Transient  //非mysql字段, 不用校验
    private List<GrantedAuthority> authorityList;

    public List<GrantedAuthority> getAuthorityList() {
        return authorityList;
    }

    public void setAuthorityList(List<GrantedAuthority> authorityList) {
        this.authorityList = authorityList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorityList;
    }

    public String getPassword() {
        return password;
    }

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

    @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 setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public Date getCreateTime() {
        return createTime;
    }

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

    public Date getLastLoginTime() {
        return lastLoginTime;
    }

    public void setLastLoginTime(Date lastLoginTime) {
        this.lastLoginTime = lastLoginTime;
    }

    public Date getLastUpdateTime() {
        return lastUpdateTime;
    }

    public void setLastUpdateTime(Date lastUpdateTime) {
        this.lastUpdateTime = lastUpdateTime;
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }
}

User类中有一个字段authorityList,这个是数据库表所没有的, 为了解决数据库检验的问题,使用@Transient申明不用校验。
@Transient //非mysql字段, 不用校验
private List<GrantedAuthority> authorityList;

package com.kason.entity;

import javax.persistence.*;

/**
 * Created by IBM on 2018/6/30.
 */
@Entity
@Table(name = "role")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "user_id")
    private Long userId;
    private String name;

    public Long getId() {
        return id;
    }

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

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

实体类之后就是repository和service
repository 类: UserRepository, RoleRepository

package com.kason.repository;

import com.kason.entity.User;
import org.springframework.data.repository.CrudRepository;

/**
 * Created by IBM on 2018/6/18.
 */
public interface UserRepository extends CrudRepository<User,Long> {

    User findUserByName(String userName);

}

package com.kason.repository;

import com.kason.entity.Role;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

/**
 * Created by IBM on 2018/6/30.
 */
public interface RoleRepository extends CrudRepository<Role, Long> {
    List<Role> findRolesByUserId(long id);
}

service类: UserServiceImpl

package com.kason.service;

import com.kason.entity.User;

/**
 *  用户服务
 * Created by IBM on 2018/6/30.
 */
public interface IUserService {

    User findUserByName(String userName);
}
package com.kason.service.user;

import com.kason.entity.Role;
import com.kason.entity.User;
import com.kason.repository.RoleRepository;
import com.kason.repository.UserRepository;
import com.kason.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;

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

/**
 * Created by IBM on 2018/6/30.
 */
@Service
public class UserServiceImpl implements IUserService {


    @Autowired
    UserRepository userRepository;
    @Autowired
    RoleRepository roleRepository;
    @Override
    public User findUserByName(String userName) {
        User user = userRepository.findUserByName(userName);
        if (null == user) {
            return null;
        }
        List<Role> rolesByUserId = roleRepository.findRolesByUserId(user.getId());
        if (null == rolesByUserId) {
            throw new DisabledException("no role");
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        rolesByUserId.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName())));
        user.setAuthorityList(authorities);

        return user;
    }
}

新建一个security 的package包:编写AuthProvider类


image.png
package com.kason.security;

import com.kason.entity.User;
import com.kason.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

/**
 * 自定义权限认真实现
 * Created by IBM on 2018/6/30.
 */
public class AuthProvider implements AuthenticationProvider{

    @Autowired
    private IUserService iUserService;

    // 因为密码是md5加密过的, 所以此处需要一个md5类解密
    private final Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = authentication.getName();
        String inputPassword = (String)authentication.getCredentials();
        // 从数据库中读取User
        User user = iUserService.findUserByName(userName);
        if (null == user) {
            throw new AuthenticationCredentialsNotFoundException("auth error");
        }
        if (this.passwordEncoder.isPasswordValid(user.getPassword(),inputPassword, user.getId())) {
            return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        } else {
            throw new BadCredentialsException("authError password not");
        }

    }

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

之后WebSecurityConfig类由原先的内存权限配置改成数据库权限配置

 package com.kason.config;

import com.kason.security.AuthProvider;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.configuration.AuthenticationConfiguration;
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;

/**
 * Created by IBM on 2018/6/30.
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     *  Http权限控制
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/login").permitAll() //admin的登陆页面允许所有用户登入
        .antMatchers("/user/login").permitAll() //用户的登陆页面允许所有人登入
        .antMatchers("/static/**").permitAll() //静态资源允许全部获取权限
        .antMatchers("/admin/**").hasRole("ADMIN") //除了/admin/login之外的/admin/**只允许admin权限用户使用
        .antMatchers("/usr/**").hasAnyRole("ADMIN","USER")
                .antMatchers("/api/user/**").hasAnyRole("ADMIN","USER")
                .and()
                .formLogin()
                .loginProcessingUrl("/login") //配置角色登陆处理入口
        .and();
        http.csrf().disable(); //方便开发
        http.headers().frameOptions().sameOrigin();
    }

    /**
     * 自定义认证策略
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
        // 内存配置, 测试使用
        //auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and();
        auth.authenticationProvider(authProvider()).eraseCredentials(true);
    }

    @Bean
    public AuthProvider authProvider() {
        return new AuthProvider();
    }
}

然后在浏览器里输入http://localhost:8081/admin/login, 密码admin, admin,可以通过权限认证,说明自定义的数据库权限认证生效了。

3 完善

3.1增加logout

.logout()
        .logoutUrl("/logout")
        .logoutSuccessUrl("/logout/page")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .and();

3.2 不同角色跳转不同登录页面

用户登录跳转用户登录页面
管理员登录跳转到管理员登录页面

  • 第一步先编写User对应的UserController
package com.kason.web.controller.user;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * Created by IBM on 2018/6/30.
 */
@Controller
public class UserController {

    @GetMapping("/user/center")
    public String userCenterPage() {
        return "user/center";
    }

    @GetMapping("/user/login")
    public String userLoginPage() {
        return "user/login";
    }
}

对应于admin的AdminController

package com.kason.web.controller.admin;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * Created by IBM on 2018/6/28.
 */
@Controller
public class AdminController {

    @GetMapping("/admin/center")
    public String adminCenterPage() {
        return "admin/center";
    }

    @GetMapping("/admin/welcome")
    public String welcomePage() {
        return "admin/welcome";
    }

    @GetMapping("/admin/login")
    public String adminLoginPage() {
        return "/admin/login";
    }
}
  • 第二步在security包下面编写LoginUrlEntryPoint 类:
package com.kason.security;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * 基于角色的登陆入口控制器
 * Created by IBM on 2018/6/30.
 */
public class LoginUrlEntryPoint extends LoginUrlAuthenticationEntryPoint {


    private final Map<String, String> authEntryPointMap;
    private PathMatcher pathMatcher = new AntPathMatcher(); // 用于进行url 模式匹配用。
    public LoginUrlEntryPoint(String loginFormUrl) {
        super(loginFormUrl);
        authEntryPointMap = new HashMap<>();
        // 普通用户登录入口映射, url模式匹配
        authEntryPointMap.put("/user/**", "/user/login");
        // 管理员登录入口映射, url模式匹配
        authEntryPointMap.put("/admin/**", "/admin/login");
    }

    /**
     * 根据请求跳转到指定的页面, 父类是默认使用的loginFormUrl
     * @param request
     * @param response
     * @param exception
     * @return
     */
    @Override
    protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
        // 首先获取要跳转的url是什么
        String replace = request.getRequestURI().replace(request.getContextPath(), ""); //本地就是去除http://localhost:8081替换成""
        // 比如http://localhost:8081/admin/center 将调到http://localhost:8081/admin/login
        // http://localhost:8081/user/center 将会跳转到http://localhost:8081/user/login
        for(Map.Entry<String, String> entry : this.authEntryPointMap.entrySet()) {

            if (this.pathMatcher.match(entry.getKey(), replace)) {
                return entry.getValue();
            }

        }
        return super.determineUrlToUseForThisRequest(request, response, exception);
    }
}
  • 第三步 WebSecurityConfig 中添加登录入口判断
.exceptionHandling()
                .authenticationEntryPoint(loginUrlEntryPoint())
                .and()

然后WebSecurityConfig就变为了:

package com.kason.config;

import com.kason.security.AuthProvider;
import com.kason.security.LoginUrlEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.configuration.AuthenticationConfiguration;
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;

/**
 * Created by IBM on 2018/6/30.
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     *  Http权限控制
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/login").permitAll() //admin的登陆页面允许所有用户登入
        .antMatchers("/user/login").permitAll() //用户的登陆页面允许所有人登入
        .antMatchers("/static/**").permitAll() //静态资源允许全部获取权限
        .antMatchers("/admin/**").hasRole("ADMIN") //除了/admin/login之外的/admin/**只允许admin权限用户使用
        .antMatchers("/user/**").hasAnyRole("ADMIN","USER")
                .antMatchers("/api/user/**").hasAnyRole("ADMIN","USER")
                .and()
                .formLogin()
                .loginProcessingUrl("/login") //配置角色登陆处理入口
        .and()
        .logout()
        .logoutUrl("/logout")
        .logoutSuccessUrl("/logout/page")
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(loginUrlEntryPoint())
                .and();
        http.csrf().disable(); //方便开发
        http.headers().frameOptions().sameOrigin();
    }

    /**
     * 自定义认证策略
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
        // 内存配置, 测试使用
        //auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN").and();
        auth.authenticationProvider(authProvider()).eraseCredentials(true);
    }

    @Bean
    public AuthProvider authProvider() {
        return new AuthProvider();
    }

    @Bean
    public LoginUrlEntryPoint loginUrlEntryPoint() {
        return new LoginUrlEntryPoint("/user/login");
    }
}

3.3 键入登录失败跳转

首先在security包下编写校验失败处理类:

package com.kason.security;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登陆失败验证处理
 * Created by IBM on 2018/6/30.
 */
public class LoginAuthFailHandler extends SimpleUrlAuthenticationFailureHandler {
    private final LoginUrlEntryPoint urlEntryPoint;

    public LoginAuthFailHandler(LoginUrlEntryPoint urlEntryPoint) {
        this.urlEntryPoint = urlEntryPoint;
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        String targetUrl = this.urlEntryPoint.determineUrlToUseForThisRequest(request, response, exception);
        targetUrl += "?" + exception.getMessage();

        super.setDefaultFailureUrl(targetUrl);

        super.onAuthenticationFailure(request, response, exception);
    }
}

然后在WebSecurityConfig login后面加入faliHander()

 .loginProcessingUrl("/login") //配置角色登陆处理入口
.failureHandler(loginAuthFailHandler())

loginAuthFailHandler方法就是实例化的bean

 @Bean
    public LoginUrlEntryPoint loginUrlEntryPoint() {
        return new LoginUrlEntryPoint("/user/login");
    }

    @Bean
    public LoginAuthFailHandler loginAuthFailHandler() {
        return new LoginAuthFailHandler(loginUrlEntryPoint());
    }

相关文章

网友评论

    本文标题:Springboot 7 权限控制

    本文链接:https://www.haomeiwen.com/subject/ctltuftx.html