使用IntelliJ IDEA+spring boot+springDataJPA+springSecurity+thymeleaf+mysql实现用户登录及权限控制

新建项目

file->new->project->Spring Initalizr->选择依赖
在这里插入图片描述

配置application.properties

#端口号(默认8080)
server.port=8083
#关闭thymeleaf缓存,方便调试
spring.thymeleaf.cache=false
#数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdemo?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#使用druid连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#自动生成表
spring.jpa.hibernate.ddl-auto=update

thymeleaf默认开启缓存,修改页面后需重启服务。
使用druid连接池需要在pom.xml中加入依赖。

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
</dependency>

创建实体类User、Role、Authority

Authority

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name","code"})})
public class Authority {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //权限名
    @Column
    private String name;
    //标识
    @Column
    private String code;
    //路径
    @Column
    private String url;
    @ManyToMany(mappedBy = "authorities")
    private List<Role> roleList=new ArrayList<>();

    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;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }
}

Role

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name"})})
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //角色名
    @Column
    private String name;
    //权限
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Authority> authorities=new ArrayList<>();
    //用户
    @ManyToMany(mappedBy = "roles")
    private List<User> userlist=new ArrayList<>();

    public List<User> getUserlist() {
        return userlist;
    }

    public void setUserlist(List<User> userlist) {
        this.userlist = userlist;
    }

    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;
    }

    public List<Authority> getAuthorities() {
        return authorities;
    }

    public void setAuthorities(List<Authority> authorities) {
        this.authorities = authorities;
    }
}

User

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"})})
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //用户名
    @Column
    private String userName;
    //密码
    @Column
    private String password;
    //角色
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Role> roles=new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> grantedAuthorities=new ArrayList<>();
        List<Role> roles = this.roles;
        for (Role role : roles) {
            List<Authority> authorities = role.getAuthorities();
            for (Authority authority : authorities) {
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_"+authority.getCode()));
            }
        }
        return grantedAuthorities;
    }

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

    @Override
    public String getUsername() {
        return this.userName;
    }
    // 帐户是否过期
    @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 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 void setPassword(String password) {
        this.password = password;
    }

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

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

    @Override
    public int hashCode() {
        return userName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return this.toString().equals(obj.toString());
    }

    @Override
    public String toString() {
        return this.userName;
    }

User需要实现security的UserDetails接口。
getAuthorities()方法是获取用户的所有权限,因为security匹配的是以ROLE_开头的权限,所以这里拼接了ROLE_。也可以在数据库插入权限的时候加ROLE_前缀。
如果设置了security用户并发登入,还需要重写User的equals、toString、hashCode方法。

创建UserDao、RoleDao、AuthorityDao

public interface UserDao extends JpaRepository<User,Long> {
}

public interface RoleDao extends JpaRepository<Role,Long> {
}

public interface AuthorityDao extends JpaRepository<Authority,Long> {

}

创建UserService、AuthorityService

@Service
public class UserServiceImpl implements UserDetailsService {
    @Autowired
    private UserDao userDao;

    /**
     * 查询用户信息
     * @param s
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = new User();
        user.setUserName(s);
        Example<User> of = Example.of(user);
        Optional<User> one = userDao.findOne(of);
        if (one.isPresent()){
            user=one.get();
        }
        return user;
    }
}
@Service
public class AuthorityServiceImpl implements AuthorityService {
    @Autowired
    private AuthorityDao authorityDao;
    @Override
    public List<Authority> findAll() {
        return authorityDao.findAll();
    }

}

UserServiceImpl需要实现UserDetailsService重写其loadUserByUsername方法,根据用户名查询用户

自定义登录成功处理器与登录失败处理器及提示信息

/**
 * 登录成功处理器
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.getWriter().print("ok");
    }
}

/**
 * 登录失败处理器
 */
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        String message="登录异常,请联系管理员";
        if (e instanceof UsernameNotFoundException ){
            message="用户不存在";
        }else if (e instanceof BadCredentialsException ){
            message="用户名或密码错误";
        }else if (e instanceof AccountExpiredException ){
            message="账户过期";
        }else if (e instanceof LockedException){
            message="账户锁定";
        }else if (e instanceof DisabledException ){
            message="账户不可用";
        }else if (e instanceof CredentialsExpiredException){
            message="证书过期";
        }
        httpServletResponse.getWriter().print(message);
    }
}

配置验证和http拦截链

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserServiceImpl userService;
    @Autowired
    private AuthorityService authorityService;
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    //配置用户验证,使用BCryptPasswordEncoder加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
    }

    //http拦截链
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //登录
        http.formLogin()                   
                .loginPage("/login")        // 设置登录页面
                .loginProcessingUrl("/login") // 自定义的登录接口
                .successHandler(myAuthenticationSuccessHandler)//登录成功处理器
                .failureHandler(myAuthenticationFailureHandler)//登录失败处理器
                .and()
                .csrf().disable();//关闭csrf
                ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = http.authorizeRequests();
                //配置不拦截的请求,其他请求拦截
                expressionInterceptUrlRegistry.antMatchers("/login", "/js/**").permitAll();
                //配置访问路径需要的权限
                List<Authority> all = authorityService.findAll();
                for (Authority authority : all) {
                    expressionInterceptUrlRegistry .antMatchers( authority.getUrl()).hasRole(authority.getCode() );
                }
                //其他请求,登陆后可以访问
                expressionInterceptUrlRegistry .anyRequest().authenticated();
        //session
        http.sessionManagement()
                .maximumSessions(1)//最大并发登录
                .expiredUrl("/login");//登录过期后跳转的url

        //注销
        http.logout()
                .logoutUrl("/logout")//触发注销的请求
                .logoutSuccessUrl("/login")//注销后跳转的请求
                .invalidateHttpSession(true);//使session失效

        //编码格式
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        http.addFilterBefore(filter, CsrfFilter.class);

    }


}

创建LoginController

@Controller
public class LoginController {
    @GetMapping("/login")
    public String login(){
        return "login";
    }
    @RequestMapping("/")
    public String index(){
        return "index";
    }
   
}

注意:return的后面不要加/,在idea中加/后可以正常访问,但是在打包运行后会报错

创建login页面和index页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<span style="color: red" id="msg"></span>
<form>
    <div>
        <label>用户名:</label><input name="username" id="username">
    </div>
    <div>
        <label>密码:</label><input name="password" id="password">
    </div>
    <div>
        <button type="button" onclick="sub()">提交</button>
    </div>


</form>
</body>
<script src="/js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
    function sub() {
        var username=$("#username").val();
        var password=$("#password").val();
        $.post("/login",{username:username,password:password},function (data) {
           if (data=="ok"){
               window.location.href="/";
           }else {
               $("#msg").text(data);
           }
        })
    }
</script>
</html>
<!DOCTYPE html>
<html  lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
首页<br><a th:href="@{/logout}">注销</a>
</body>

</html>

自定义403页面

在templates下创建error文件夹,创建403.html。当出现403错误时会自动跳转到此页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
无权限
</body>
</html>
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值